Compare commits

..

103 Commits

Author SHA1 Message Date
Matthias Clasen 1cc3c019e6 Include precompiled versions of .ui files
These are smaller, and a faster to process than .ui files.
2021-12-14 01:51:22 -05:00
Matthias Clasen 319aa558a5 buildertool: Add a precompile command
This writes out the precompiled format of .ui
files that we are using internally.
2021-12-14 01:51:22 -05:00
Matthias Clasen 208769f70f Merge branch 'optimize-precompile' into 'main'
builder: Use a string chunk for precompile

See merge request GNOME/gtk!4233
2021-12-14 06:34:49 +00:00
Matthias Clasen c5bffb9fb5 builder: Drop empty text chunks when precompiling
These don't add any value either.
2021-12-14 01:21:06 -05:00
Matthias Clasen c7afa5452b builder: Drop irrelevant whitespace in precompile
Drop text nodes that won't contribute to the end result.
This gets rid of a lot of text nodes in the replay.
2021-12-14 00:35:35 -05:00
Matthias Clasen c7c6e83779 Merge branch 'matthiasc/for-main' into 'main'
builder: Use GMarkup properly

See merge request GNOME/gtk!4250
2021-12-14 04:58:06 +00:00
Garrett Regier fcb6adaaaa builder: Use g_slice_free_chain() for strings in precompile 2021-12-13 23:44:02 -05:00
Garrett Regier 9c12b58e32 builder: Remove root special case from precompile 2021-12-13 23:44:02 -05:00
Garrett Regier 6c8b505f93 builder: Avoid g_hash_table_get_values() in precompile
Embed the GList link in the RecordDataString.
2021-12-13 23:44:02 -05:00
Garrett Regier 8b228e7471 builder: Use a flexible array for attributes in precompile 2021-12-13 23:44:02 -05:00
Garrett Regier 4ce07f41ca builder: Reduce memory usage in precompile
Split the Element and Text nodes into separate structures.
2021-12-13 23:44:02 -05:00
Garrett Regier 1bfd0e5e38 builder: Use a GQueue in precompile
This avoids g_list_last() and
embeds the GList link in the RecordDataTree.
2021-12-13 23:44:02 -05:00
Garrett Regier b962d37f79 builder: Use a reasonable default string size in precompile 2021-12-13 23:44:02 -05:00
Garrett Regier c6ecf02a07 builder: Embed text length in precompile 2021-12-13 23:44:02 -05:00
Garrett Regier 8b7d4b423c builder: Combine attribute name and value allocations 2021-12-13 23:44:02 -05:00
Garrett Regier 83dc126565 builder: Use a string chunk for precompile
Also use an explicit length and avoid g_strndup().
2021-12-13 23:44:02 -05:00
Garrett Regier f2d3d7e710 builder: Avoid double string lookup in precompile 2021-12-13 23:44:02 -05:00
Matthias Clasen f991428cb9 builder: Use GMarkup properly
We must call end_parse to ensure we get errors
for incomplete documents.
2021-12-13 23:42:47 -05:00
Matthias Clasen c9cf7b1e08 Merge branch 'matthiasc/for-main' into 'main'
tiff loader: Catch more errors

See merge request GNOME/gtk!4248
2021-12-14 00:31:49 +00:00
Matthias Clasen 4d865cd7ba tiff loader: Catch more errors
tiff_open_read may fail, and we should not crash
in that case but return an error.
2021-12-13 17:54:11 -05:00
Matthias Clasen a55458a84a Merge branch 'builder-tool-fixes' into 'main'
builderparser: Be more robust

See merge request GNOME/gtk!4247
2021-12-13 21:29:48 +00:00
Matthias Clasen 66d8631c23 buildertool: Be more robust
Don't pass a NULL string to gtk_builder_value_from_string.
2021-12-13 15:09:48 -05:00
Matthias Clasen 5c1ad88137 builderparser: Be more robust
If a document contains no useful content,
just say so instead of crashing.
2021-12-13 15:09:48 -05:00
Matthias Clasen e230c9c6f0 Merge branch 'builder-tool-fixes' into 'main'
buildertool: Fix a possible crash

See merge request GNOME/gtk!4246
2021-12-13 19:54:56 +00:00
Matthias Clasen 6d24a2c942 buildertool: Be more robust
A lot of crashes in simplify can be avoided
if get_class_name does not return NULL.
2021-12-13 14:33:19 -05:00
Matthias Clasen 5222dc0cd1 buildertool: Fix a possible crash
We need to call g_markup_parse_context_end_parse
to catch incomplete documents that we might not
handle well later.
2021-12-13 13:52:50 -05:00
Yuri Chornoivan 51a72a9c53 Update Ukrainian translation 2021-12-13 13:16:42 +00:00
Matthias Clasen f609d9cd59 Merge branch 'wip/baedert/for-master' into 'main'
gl: Imply that node_supports_transforms() means 2D transforms

Closes #4501

See merge request GNOME/gtk!4232
2021-12-13 12:55:04 +00:00
Matthias Clasen 3901c6ab91 Merge branch 'fix-x11-drag-icons' into 'main'
colorswatch: Make drag source optional

See merge request GNOME/gtk!4244
2021-12-13 03:39:48 +00:00
Matthias Clasen da6f86bd79 colorbutton: No nested drag sources, please
This is causing trouble on X11.
2021-12-12 22:23:31 -05:00
Matthias Clasen 42fd499af4 dragicon: Make color icons work in X11
We need to disable the DND support in
GtkColorSwatch completely for this to work
in X11.
2021-12-12 22:09:28 -05:00
Matthias Clasen de5b88477a colorswatch: Make drag source optional
When using a colorswatch as a drag icon,
this can get in the way, so make it optional.
2021-12-12 22:09:16 -05:00
Matthias Clasen cd49a7f9e9 Merge branch 'matthiasc/for-main' into 'main'
dragicon: Provide default icons for paintables

See merge request GNOME/gtk!4243
2021-12-13 02:43:48 +00:00
Matthias Clasen df025fcb88 dragicon: Provide default icons for more types
Provide default icons for paintables and files.
This is easy to do, and makes sense.

fixup drag icon
2021-12-12 21:29:36 -05:00
Matthias Clasen bbb1404bd3 dragicon: No events, please
We run into trouble on X11 if the widgets
in the drag icon have drop targets attached.
Prevent this by suppressing event delivery
to drag icons outright.
2021-12-12 21:28:34 -05:00
Benjamin Otte af2a172197 Merge branch 'wip/otte/for-main' into 'main'
x11: Fix a deadlock in INCR transfers

See merge request GNOME/gtk!4242
2021-12-13 01:17:00 +00:00
Benjamin Otte 252b9fc39c x11: Don't delete important signal handlers randomly
We finish the write to the output stream long after the stream has been
closed, so we want to keep the event handler around to do just that.

Instead, remove the handler on finalize.
2021-12-13 01:54:21 +01:00
Benjamin Otte 6fc5e04d7c x11: Explicitly track end of stream
The OutputStream needs to write a 0 byte end of stream Property. We need
to track if that has been written, and we do that with that new
property.

We also use that property to always request flushes when the stream is
being closed, so that we don't wait for another flush() call.
2021-12-13 01:52:30 +01:00
Benjamin Otte 66f1fef083 x11: Explicitly close_async() the output stream
We need to be very careful when writing data, because if we aren't, sync
functions will be called on the output stream and X11 does not like that
at all.
2021-12-13 01:51:35 +01:00
Benjamin Otte 645d4807c3 x11: Keep a reference to the SelectionOutputStream while writing
This ensures close() isn't called from dispose() while we're still busy
writing.

In theory this should never happen, but in practice it just did.
2021-12-13 01:42:45 +01:00
Matthias Clasen e2c360ada0 Merge branch 'clipboard-demo-improvements' into 'main'
inspector: Show more datatypes for the clipboard

See merge request GNOME/gtk!4241
2021-12-12 23:51:30 +00:00
Matthias Clasen a463ead739 gdk: Drop some pointless code
There is no point in sorting png first when
registering (de)serializers, since we ignore
the png format now when walking the list.
2021-12-12 18:36:58 -05:00
Matthias Clasen dbe5e57b8e inspector: Show more datatypes for the clipboard
It is easy enough to show colors, files and pixbufs.
2021-12-12 18:36:58 -05:00
Matthias Clasen 99b99d7b23 Merge branch 'clipboard-demo-improvements' into 'main'
droptarget: Improve the docs

See merge request GNOME/gtk!4240
2021-12-12 21:42:31 +00:00
Matthias Clasen 787111a145 gtk-demo: Add dnd back to the clipboard demo 2021-12-12 16:27:36 -05:00
Matthias Clasen 9a872f059f droptarget: Improve the docs
Add the signal connection to the example.
2021-12-12 16:27:36 -05:00
Matthias Clasen eadc94e0a1 Merge branch 'clipboard-demo-improvements' into 'main'
gtk-demo: Small followup fixes

See merge request GNOME/gtk!4239
2021-12-12 18:58:07 +00:00
Matthias Clasen f8855e892a gtk-demo: Small followup fixes
Make image copy work remotely.
2021-12-12 13:41:24 -05:00
Matthias Clasen 27d05014e3 Merge branch 'new-clipboard-demo' into 'main'
docs: Small improvement

See merge request GNOME/gtk!4238
2021-12-12 18:30:27 +00:00
Matthias Clasen ebb58b7cbc gtk-demo: Rewrite the clipboard demo
This is more in line with the current clipboard
api, and shows handling colors and files.
2021-12-12 13:13:11 -05:00
Yuri Chornoivan 7f6895a4bb Update Ukrainian translation 2021-12-12 17:18:14 +00:00
Hugo Carvalho 80ae4c1a69 Update Portuguese translation 2021-12-12 16:26:05 +00:00
Matthias Clasen e72df9cd5f docs: Small improvement
I could not find the attributes of the <binding>
element mentioned anywhere in the docs.
2021-12-12 09:03:11 -05:00
Piotr Drąg 80ba529fb9 Update POTFILES.in and POTFILES.skip 2021-12-12 13:54:41 +01:00
Timm Bäder fd6b3ef5a0 gl: Linear gradients don't support 3d transforms
Add another helper similar to the one for transforms, but that only
works on 2d transforms.

Fixes #4501
2021-12-12 12:57:44 +01:00
Benjamin Otte 4cd0a39794 Merge branch 'wip/otte/inspector' into 'main'
inspector: Add a clipboard page

See merge request GNOME/gtk!4237
2021-12-11 22:46:30 +00:00
Benjamin Otte 5fbc510f94 contentformats: Make sure 0 elements means the array is NULL
Don't return arrays with 0 elements, return NULL.
2021-12-11 23:21:53 +01:00
Benjamin Otte 600ab5ba5f inspector: Add a clipboard page
Shows all the formats supported by the clipboard (and primary clipboard)
and allows displaying them (by potentially downloading them)
2021-12-11 23:01:42 +01:00
Matthias Clasen ae92181d02 Merge branch 'fix-textchild-abi' into 'main'
Restore the GtkTextchild abi

Closes #4531

See merge request GNOME/gtk!4236
2021-12-11 16:32:45 +00:00
Matthias Clasen 66910ed998 Restore the GtkTextchild abi
Move the new chars field to a private struct.

Fixes: #4531
2021-12-11 10:58:58 -05:00
Matthias Clasen 5371e4403e Merge branch 'deprecate-device-source' into 'main'
Deprecate GdkDevice:source

See merge request GNOME/gtk!4207
2021-12-10 18:35:00 +00:00
Matthias Clasen 79375dd7dd Merge branch 'fix-printer-enumeration' into 'main'
Fix dialog-less printing

Closes #4439

See merge request GNOME/gtk!4215
2021-12-10 18:01:59 +00:00
Matthias Clasen 6889609ab9 Merge branch 'bump-meson-dep' into 'main'
Require meson 0.59

Closes #4486

See merge request GNOME/gtk!4230
2021-12-10 18:00:50 +00:00
Matthias Clasen 0bf87281e0 Merge branch 'missing-since-tag' into 'main'
Add a missing since tag

See merge request GNOME/gtk!4229
2021-12-10 17:15:32 +00:00
Matthias Clasen bc7bed7517 ci: Use meson 0.59 for msvc 2021-12-10 12:14:04 -05:00
Matthias Clasen cfac6fd752 ci: Use meson 0.59 on macos 2021-12-10 12:12:52 -05:00
Matthias Clasen 97c09c827b Fix the targets variable in pc files
This is meant as a space-separated list of
string, so escaping the spaces is uncalled
for.

Fixes: #4486
2021-12-10 11:36:07 -05:00
Matthias Clasen 0370672886 Require meson 0.59
This is needed to fix a regression in our pc file
generation.
2021-12-10 11:34:23 -05:00
Matthias Clasen 2d062fedd9 Add a missing since tag
gtk_text_child_anchor_new_with_replacement was
recently added.
2021-12-10 11:26:46 -05:00
Matthias Clasen c6a68f3de2 Fix dialog-less printing
We were sometimes ending printer enumeration prematurely,
and the code was confused about the meaning of found_printer.

The new setup follows these rules:
- We *only* end the search prematurely if found_printer
  is set, which indicates that we found the right printer
- We *always* call find_printer_idle exactly once, and
  make it return less than perfect matches like the
  default printer, or the first printer we found

Fixes: #4439
2021-12-10 11:26:23 -05:00
Matthias Clasen d6181b2335 Merge branch 'wip/jimmac/unfocused-selection-dejavu' into 'main'
Revert "styling: Have unfocused selections"

Closes #4393

See merge request GNOME/gtk!4228
2021-12-10 13:17:32 +00:00
Jakub Steiner d74e62886c selections: have unfocused state
- text selections for unfocused elements - textview, label, entry,
  spinbutton

fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/4393
2021-12-10 12:00:12 +01:00
Jakub Steiner d8505f09eb Revert "styling: Have unfocused selections"
This reverts commit 96f63a6bf3.
2021-12-10 11:36:10 +01:00
Matthias Clasen 935c6aade0 Merge branch 'bilelmoussaoui/header' into 'main'
gdk: drop removed method from the header

See merge request GNOME/gtk!4225
2021-12-09 23:50:21 +00:00
Matthias Clasen a35c35f849 Merge branch 'wip/chergert/fix-macos-crash' into 'main'
macos: fix crash in imcontextquartz

See merge request GNOME/gtk!4226
2021-12-09 23:49:39 +00:00
Bilal Elmoussaoui 57518a2bb4 gdk: drop removed method from the header 2021-12-09 20:29:17 +00:00
Christian Hergert 99a8202015 macos: fix crash in imcontextquartz 2021-12-09 12:26:33 -08:00
Benjamin Otte 17c903e246 Merge branch 'fix-picture-natural-size-0' into 'main'
picture: Return natural size 0 for size 0

See merge request GNOME/gtk!4177
2021-12-08 19:33:28 +00:00
Benjamin Otte 4de5d225db reftests: Add center-center reftests without GtkOverlay 2021-12-08 22:16:52 +03:00
Benjamin Otte 7741df9963 reftests: Rename a bunch of tests
They use a GtkOverlay, so reflect that in the name.
2021-12-08 22:16:52 +03:00
Ivan Molodetskikh 7ef54e9c53 picture: Return natural size 0 for size 0
It was returning the full natural size (same as -1) due to default value
handling in the code below.

Tests have been updated to match this output.
2021-12-08 19:20:24 +01:00
Matthias Clasen 12bb700c62 Merge branch 'wip/chergert/fix-recursive-action-muxer' into 'main'
actionmuxer: avoid duplicate and recursive work

Closes #4422, #4519, and gnome-text-editor#220

See merge request GNOME/gtk!4223
2021-12-08 10:47:40 +00:00
Matthias Clasen e3ba7250f1 Merge branch 'wip/chergert/layout-marshaller' into 'main'
surface: add missing va_marshaller for layout signal

See merge request GNOME/gtk!4221
2021-12-08 10:46:35 +00:00
Matthias Clasen 54842095c3 Merge branch 'wip/chergert/reduce-action-muxer-allocations' into 'main'
menutracker: reduce allocations and signal emission

See merge request GNOME/gtk!4222
2021-12-08 10:45:56 +00:00
Christian Hergert d1aec0c3f1 actionmuxer: avoid duplicate and recursive work
If we've already done the tracking into the parent muxer, there is no need
to do it again. This can save a great deal of recursive work when adding
items to the muxer.

This makes showing the context menu in gnome-text-editor repeatedly fast
even as spelling corrections are changed.

It is likely that this could fix #4422 as well.

Fixes #4519
Fixes https://gitlab.gnome.org/GNOME/gnome-text-editor/-/issues/220
2021-12-08 00:32:36 -08:00
Christian Hergert 00d5f72d6e menutracker: reduce allocations and signal emission
When handling action-added callbacks the code was previously using a
freeze_notify/thaw_notify in all cases. This turns out to allocate a
significant amount of memory when called a lot.

That said, it shouldn't be getting called this much but given the current
state of affairs elsewhere in GtkActionMuxer, this brought temporary
allocations down from 9MiB to 9KiB in gnome-text-editor after showing
the context menu a few times.

Related #4422
2021-12-07 22:42:04 -08:00
Christian Hergert 15d01d4315 surface: add missing va_marshaller for layout signal
I saw this coming across through a ffi boundary in Sysprof, and we wanted
to keep most things within GDK using native marshalling to improve
profiler results when frame pointers are not used.
2021-12-07 22:21:19 -08:00
Benjamin Otte 348e34f221 Merge branch 'wip/otte/viewport' into 'main'
viewport: Handle things differently

See merge request GNOME/gtk!4220
2021-12-07 23:31:06 +00:00
Benjamin Otte ce7b0656c0 viewport: Allocate properly
Instead of allocation width for height for width for height or whatever
that code was doing, actually allocate the size we were given or the
requested size, whatever is larger.
2021-12-08 00:12:35 +01:00
Benjamin Otte fd0d360f9b Revert "viewport: Actually report the size we're gonna allocate"
This reverts commit b8468af411.
2021-12-07 23:52:21 +01:00
Benjamin Otte f26cae3838 Merge branch 'wip/otte/viewport' into 'main'
viewport: Use array for member variables

See merge request GNOME/gtk!4219
2021-12-07 21:19:58 +00:00
Benjamin Otte b8468af411 viewport: Actually report the size we're gonna allocate
Don't just pass on measure() calls, but actually behave in the way we
behave during size allocate.

This should improve cases where GtkScrolledWindow is used with GTK_POLICY_NEVER.
2021-12-07 21:58:42 +01:00
Benjamin Otte 9fd7e319f3 viewport: Use array for member variables
That way we can index them by orientation in future commits.
2021-12-07 21:58:42 +01:00
Benjamin Otte 80a8b59f24 Merge branch 'wip/otte/for-main' into 'main'
gtktypes: GTK_INVALID_LIST_POSITION should be an int

See merge request GNOME/gtk!4218
2021-12-07 15:30:48 +00:00
Benjamin Otte b9d4da9cfe gtktypes: GTK_INVALID_LIST_POSITION should be an int
GListModel uses guint, so the macros we define for it should match that.

Related: !3738
2021-12-07 16:15:17 +01:00
Emmanuele Bassi 5bf5b58eb3 Merge branch 'gi-const' into 'main'
gtk: fix GTK_INVALID_LIST_POSITION type

See merge request GNOME/gtk!3738
2021-12-07 14:22:40 +00:00
Hugo Carvalho b2c227e9c5 Update Portuguese translation 2021-12-04 19:46:16 +00:00
Yuri Chornoivan d854228d58 Update Ukrainian translation 2021-12-04 16:46:08 +00:00
Benjamin Otte a0c09bc2a9 Merge branch 'check-for-unknown-duration' into 'main'
gstmediafile: Correctly report unknown duration

See merge request GNOME/gtk!4217
2021-12-04 09:45:34 +00:00
Anders Jonsson aca3b2da58 Update Swedish translation 2021-12-04 09:34:37 +00:00
Ivan Molodetskikh 604541863c gstmediafile: Correctly report unknown duration
When loading .mp3 files the duration is initially unknown. Before this
change it was reported as a large integer (since GST_CLOCK_TIME_NONE is
-1). Now it's correctly reported as 0.
2021-12-04 11:48:30 +03:00
Matthias Clasen fd9e0dd13a Deprecate GdkDevice:source
GdkDeviceTool is more consistently supported across
backends, and provides the same information.
2021-12-01 14:04:35 -05:00
Marc-André Lureau 42249ce28e gtk: fix GTK_INVALID_LIST_POSITION type
Unless there is an explicit front cast, constants are generally int for g-i.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2021-07-08 13:08:43 +04:00
317 changed files with 4383 additions and 41645 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ macos:
needs: []
before_script:
- bash .gitlab-ci/show-info-osx.sh
- pip3 install --user meson==0.56
- pip3 install --user meson==0.59
- pip3 install --user ninja
- export PATH=/Users/gitlabrunner/Library/Python/3.7/bin:$PATH
- export MESON_FORCE_BACKTRACE=1
+1 -1
View File
@@ -5,7 +5,7 @@ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliar
@echo on
:: FIXME: make warnings fatal
pip3 install --upgrade --user meson==0.56.2 || goto :error
pip3 install --upgrade --user meson==0.59 || goto :error
meson -Dmedia-gstreamer=disabled _build || goto :error
ninja -C _build || goto :error
+316 -127
View File
@@ -1,12 +1,10 @@
/* Clipboard
*
* GdkClipboard is used for clipboard handling. This demo shows how to
* copy and paste text to and from the clipboard.
* copy and paste text, images, colors or files to and from the clipboard.
*
* It also shows how to transfer images via the clipboard or via
* drag-and-drop, and how to make clipboard contents persist after
* the application exits. Clipboard persistence requires a clipboard
* manager to run.
* You can also use Drag-And-Drop to copy the data from the source to the
* target.
*/
#include <glib/gi18n.h>
@@ -14,22 +12,103 @@
#include <string.h>
#include "demoimage.h"
static GtkWidget *window = NULL;
static void
copy_button_clicked (GtkStack *source_stack,
gpointer user_data)
{
GdkClipboard *clipboard;
const char *visible_child_name;
GtkWidget *visible_child;
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (source_stack));
visible_child = gtk_stack_get_visible_child (source_stack);
visible_child_name = gtk_stack_get_visible_child_name (source_stack);
if (strcmp (visible_child_name, "Text") == 0)
{
gdk_clipboard_set_text (clipboard, gtk_editable_get_text (GTK_EDITABLE (visible_child)));
}
else if (strcmp (visible_child_name, "Image") == 0)
{
GtkWidget *child;
for (child = gtk_widget_get_first_child (visible_child); child; child = gtk_widget_get_next_sibling (child))
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (child)))
{
GtkWidget *image = gtk_widget_get_first_child (child);
GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
if (GDK_IS_TEXTURE (paintable))
gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, paintable);
else
gdk_clipboard_set (clipboard, GDK_TYPE_PAINTABLE, paintable);
break;
}
}
}
else if (strcmp (visible_child_name, "Color") == 0)
{
GdkRGBA color;
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (visible_child), &color);
gdk_clipboard_set (clipboard, GDK_TYPE_RGBA, &color);
}
else if (strcmp (visible_child_name, "File") == 0)
{
gdk_clipboard_set (clipboard, G_TYPE_FILE, g_object_get_data (G_OBJECT (visible_child), "file"), NULL);
}
else
{
g_print ("TODO");
}
}
static void
copy_button_clicked (GtkWidget *button,
gpointer user_data)
present_value (GtkStack *dest_stack,
const GValue *value)
{
GtkWidget *entry;
GdkClipboard *clipboard;
GtkWidget *child;
entry = GTK_WIDGET (user_data);
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
{
GFile *file;
/* Get the clipboard object */
clipboard = gtk_widget_get_clipboard (entry);
gtk_stack_set_visible_child_name (dest_stack, "File");
child = gtk_stack_get_visible_child (dest_stack);
/* Set clipboard text */
gdk_clipboard_set_text (clipboard, gtk_editable_get_text (GTK_EDITABLE (entry)));
file = g_value_get_object (value);
g_object_set (child, "label", g_file_peek_path (file), NULL);
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA))
{
GdkRGBA *color;
gtk_stack_set_visible_child_name (dest_stack, "Color");
child = gtk_widget_get_first_child (gtk_stack_get_visible_child (dest_stack));
color = g_value_get_boxed (value);
g_object_set (child, "rgba", color, NULL);
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE) ||
G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE))
{
GdkPaintable *paintable;
gtk_stack_set_visible_child_name (dest_stack, "Image");
child = gtk_stack_get_visible_child (dest_stack);
paintable = g_value_get_object (value);
g_object_set (child, "paintable", paintable, NULL);
}
else if (G_VALUE_HOLDS (value, G_TYPE_STRING))
{
gtk_stack_set_visible_child_name (dest_stack, "Text");
child = gtk_stack_get_visible_child (dest_stack);
gtk_label_set_label (GTK_LABEL (child), g_value_get_string (value));
}
}
static void
@@ -37,149 +116,259 @@ paste_received (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GtkStack *dest_stack = user_data;
GdkClipboard *clipboard;
GtkWidget *entry;
char *text;
const GValue *value;
GError *error = NULL;
clipboard = GDK_CLIPBOARD (source_object);
entry = GTK_WIDGET (user_data);
/* Get the resulting text of the read operation */
text = gdk_clipboard_read_text_finish (clipboard, result, &error);
if (text)
value = gdk_clipboard_read_value_finish (clipboard, result, &error);
if (value)
{
/* Set the entry text */
gtk_editable_set_text (GTK_EDITABLE (entry), text);
g_free (text);
present_value (dest_stack, value);
}
else
{
GtkWidget *dialog;
/* Show an error about why pasting failed.
* Usually you probably want to ignore such failures,
* but for demonstration purposes, we show the error.
*/
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"Could not paste text: %s",
error->message);
g_signal_connect (dialog, "response",
G_CALLBACK (gtk_window_destroy), NULL);
gtk_widget_show (dialog);
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
paste_button_clicked (GtkWidget *button,
gpointer user_data)
paste_button_clicked (GtkStack *dest_stack,
gpointer user_data)
{
GtkWidget *entry;
GdkClipboard *clipboard;
GdkContentFormats *formats;
entry = GTK_WIDGET (user_data);
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (dest_stack));
formats = gdk_clipboard_get_formats (clipboard);
/* Get the clipboard object */
clipboard = gtk_widget_get_clipboard (entry);
if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE))
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_TEXTURE, 0, NULL, paste_received, dest_stack);
else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE))
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_PAINTABLE, 0, NULL, paste_received, dest_stack);
else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA))
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_RGBA, 0, NULL, paste_received, dest_stack);
else if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE))
gdk_clipboard_read_value_async (clipboard, G_TYPE_FILE, 0, NULL, paste_received, dest_stack);
else if (gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
gdk_clipboard_read_value_async (clipboard, G_TYPE_STRING, 0, NULL, paste_received, dest_stack);
}
/* Request the contents of the clipboard, contents_received will be
called when we do get the contents.
*/
gdk_clipboard_read_text_async (clipboard, NULL, paste_received, entry);
static void
update_copy_button_sensitivity (GtkWidget *source_stack)
{
GtkButton *copy_button;
const char *visible_child_name;
GtkWidget *visible_child;
gboolean sensitive;
copy_button = GTK_BUTTON (g_object_get_data (G_OBJECT (source_stack), "copy-button"));
visible_child = gtk_stack_get_visible_child (GTK_STACK (source_stack));
visible_child_name = gtk_stack_get_visible_child_name (GTK_STACK (source_stack));
if (strcmp (visible_child_name, "Text") == 0)
{
sensitive = strlen (gtk_editable_get_text (GTK_EDITABLE (visible_child))) > 0;
}
else if (strcmp (visible_child_name, "Color") == 0 ||
strcmp (visible_child_name, "Image") == 0)
{
sensitive = TRUE;
}
else if (strcmp (visible_child_name, "File") == 0)
{
sensitive = g_object_get_data (G_OBJECT (visible_child), "file") != NULL;
}
else
{
sensitive = FALSE;
}
gtk_widget_set_sensitive (GTK_WIDGET (copy_button), sensitive);
}
static void
source_changed_cb (GtkButton *copy_button,
GParamSpec *pspec,
GtkWidget *source_stack)
{
update_copy_button_sensitivity (source_stack);
}
static void
text_changed_cb (GtkButton *copy_button,
GParamSpec *pspec,
GtkWidget *entry)
{
update_copy_button_sensitivity (gtk_widget_get_ancestor (entry, GTK_TYPE_STACK));
}
static void
file_button_set_file (GtkButton *button,
GFile *file)
{
gtk_label_set_label (GTK_LABEL (gtk_button_get_child (button)), g_file_peek_path (file));
g_object_set_data_full (G_OBJECT (button), "file", g_object_ref (file), g_object_unref);
}
static void
file_chooser_response (GtkNativeDialog *dialog,
int response,
GtkButton *button)
{
gtk_native_dialog_hide (dialog);
if (response == GTK_RESPONSE_ACCEPT)
{
GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
file_button_set_file (button, file);
g_object_unref (file);
update_copy_button_sensitivity (gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_STACK));
}
gtk_native_dialog_destroy (dialog);
}
static void
open_file_cb (GtkWidget *button)
{
GtkFileChooserNative *chooser;
chooser = gtk_file_chooser_native_new ("Choose a file",
GTK_WINDOW (gtk_widget_get_ancestor (button, GTK_TYPE_WINDOW)),
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Open",
"_Cancel");
g_signal_connect (chooser, "response", G_CALLBACK (file_chooser_response), button);
gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser));
}
static void
update_paste_button_sensitivity (GdkClipboard *clipboard,
GtkWidget *paste_button)
{
GdkContentFormats *formats;
gboolean sensitive = FALSE;
formats = gdk_clipboard_get_formats (clipboard);
if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE) ||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA) ||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE) ||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE) ||
gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
sensitive = TRUE;
gtk_widget_set_sensitive (paste_button, sensitive);
}
static void
unset_clipboard_handler (gpointer data)
{
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
g_signal_handlers_disconnect_by_func (clipboard, update_paste_button_sensitivity, data);
}
static gboolean
on_drop (GtkStack *dest_stack,
const GValue *value,
double x,
double y,
gpointer data)
{
present_value (dest_stack, value);
return TRUE;
}
static GdkContentProvider *
drag_prepare (GtkDragSource *drag_source,
double x,
double y,
gpointer data)
{
GtkWidget *button;
GValue value = G_VALUE_INIT;
button = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
if (GTK_IS_TOGGLE_BUTTON (button))
{
GtkWidget *image = gtk_widget_get_first_child (button);
GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
if (GDK_IS_TEXTURE (paintable))
{
g_value_init (&value, GDK_TYPE_TEXTURE);
g_value_set_object (&value, paintable);
}
else
{
g_value_init (&value, GDK_TYPE_PAINTABLE);
g_value_set_object (&value, paintable);
}
}
else
{
GFile *file = g_object_get_data (G_OBJECT (button), "file");
if (file)
{
g_value_init (&value, G_TYPE_FILE);
g_value_set_object (&value, file);
}
else
return NULL;
}
return gdk_content_provider_new_for_value (&value);
}
GtkWidget *
do_clipboard (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *vbox, *hbox;
GtkWidget *label;
GtkWidget *entry, *button;
GtkWidget *image;
GtkBuilderScope *scope;
GtkBuilder *builder;
GtkWidget *button;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Clipboard");
scope = gtk_builder_cscope_new ();
gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
"copy_button_clicked", G_CALLBACK (copy_button_clicked),
"paste_button_clicked", G_CALLBACK (paste_button_clicked),
"source_changed_cb", G_CALLBACK (source_changed_cb),
"text_changed_cb", G_CALLBACK (text_changed_cb),
"open_file_cb", G_CALLBACK (open_file_cb),
"on_drop", G_CALLBACK (on_drop),
"drag_prepare", G_CALLBACK (drag_prepare),
NULL);
builder = gtk_builder_new ();
gtk_builder_set_scope (builder, scope);
gtk_builder_add_from_resource (builder, "/clipboard/clipboard.ui", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, 8);
gtk_widget_set_margin_end (vbox, 8);
gtk_widget_set_margin_top (vbox, 8);
gtk_widget_set_margin_bottom (vbox, 8);
button = GTK_WIDGET (gtk_builder_get_object (builder, "copy_button"));
g_object_set_data (gtk_builder_get_object (builder, "source_stack"), "copy-button", button);
gtk_window_set_child (GTK_WINDOW (window), vbox);
button = GTK_WIDGET (gtk_builder_get_object (builder, "paste_button"));
g_signal_connect (gtk_widget_get_clipboard (button), "changed",
G_CALLBACK (update_paste_button_sensitivity), button);
g_object_set_data_full (G_OBJECT (button), "clipboard-handler", button, unset_clipboard_handler);
label = gtk_label_new ("\"Copy\" will copy the text\nin the entry to the clipboard");
gtk_box_append (GTK_BOX (vbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_widget_set_margin_start (hbox, 8);
gtk_widget_set_margin_end (hbox, 8);
gtk_widget_set_margin_top (hbox, 8);
gtk_widget_set_margin_bottom (hbox, 8);
gtk_box_append (GTK_BOX (vbox), hbox);
/* Create the first entry */
entry = gtk_entry_new ();
gtk_box_append (GTK_BOX (hbox), entry);
/* Create the button */
button = gtk_button_new_with_mnemonic (_("_Copy"));
gtk_box_append (GTK_BOX (hbox), button);
g_signal_connect (button, "clicked",
G_CALLBACK (copy_button_clicked), entry);
label = gtk_label_new ("\"Paste\" will paste the text from the clipboard to the entry");
gtk_box_append (GTK_BOX (vbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_widget_set_margin_start (hbox, 8);
gtk_widget_set_margin_end (hbox, 8);
gtk_widget_set_margin_top (hbox, 8);
gtk_widget_set_margin_bottom (hbox, 8);
gtk_box_append (GTK_BOX (vbox), hbox);
/* Create the second entry */
entry = gtk_entry_new ();
gtk_box_append (GTK_BOX (hbox), entry);
/* Create the button */
button = gtk_button_new_with_mnemonic (_("_Paste"));
gtk_box_append (GTK_BOX (hbox), button);
g_signal_connect (button, "clicked",
G_CALLBACK (paste_button_clicked), entry);
label = gtk_label_new ("Images can be transferred via the clipboard, too");
gtk_box_append (GTK_BOX (vbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_widget_set_margin_start (hbox, 8);
gtk_widget_set_margin_end (hbox, 8);
gtk_widget_set_margin_top (hbox, 8);
gtk_widget_set_margin_bottom (hbox, 8);
gtk_box_append (GTK_BOX (vbox), hbox);
/* Create the first image */
image = demo_image_new ("dialog-warning");
gtk_box_append (GTK_BOX (hbox), image);
/* Create the second image */
image = demo_image_new ("process-stop");
gtk_box_append (GTK_BOX (hbox), image);
/* Create the third image */
image = demo_image_new ("weather-clear");
gtk_box_append (GTK_BOX (hbox), image);
g_object_unref (builder);
g_object_unref (scope);
}
if (!gtk_widget_get_visible (window))
+288
View File
@@ -0,0 +1,288 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="resizable">1</property>
<property name="title">Clipboard</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel">
<property name="label">“Copy” will copy the selected data the clipboard, “Paste” will show the current clipboard contents. You can also drag the data to the bottom.</property>
<property name="wrap">1</property>
<property name="max-width-chars">40</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">12</property>
<child>
<object class="GtkDropDown" id="source_chooser">
<property name="valign">center</property>
<property name="model">
<object class="GtkStringList">
<items>
<item>Text</item>
<item>Color</item>
<item>Image</item>
<item>File</item>
</items>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStack" id="source_stack">
<signal name="notify::visible-child" handler="source_changed_cb" object="copy_button"/>
<property name="vexpand">1</property>
<binding name="visible-child-name">
<lookup name="string" type="GtkStringObject">
<lookup name="selected-item">
source_chooser
</lookup>
</lookup>
</binding>
<child>
<object class="GtkStackPage">
<property name="name">Text</property>
<property name="child">
<object class="GtkEntry" id="source_text">
<property name="valign">center</property>
<signal name="notify::text" handler="text_changed_cb" object="copy_button"/>
<property name="text">Copy this!</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">Color</property>
<property name="child">
<object class="GtkColorButton" id="source_color">
<property name="valign">center</property>
<property name="rgba">purple</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">Image</property>
<property name="child">
<object class="GtkBox">
<property name="valign">center</property>
<style>
<class name="linked"/>
</style>
<child>
<object class="GtkToggleButton" id="image_rose">
<property name="active">1</property>
<child>
<object class="GtkDragSource">
<signal name="prepare" handler="drag_prepare"/>
</object>
</child>
<child>
<object class="GtkImage">
<style>
<class name="large-icons"/>
</style>
<property name="paintable">resource:///transparent/portland-rose.jpg</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkToggleButton" id="image_floppy">
<property name="group">image_rose</property>
<child>
<object class="GtkDragSource">
<signal name="prepare" handler="drag_prepare"/>
</object>
</child>
<child>
<object class="GtkImage">
<style>
<class name="large-icons"/>
</style>
<property name="paintable">resource:///images/floppybuddy.gif</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkToggleButton" id="image_logo">
<property name="group">image_floppy</property>
<child>
<object class="GtkDragSource">
<signal name="prepare" handler="drag_prepare"/>
</object>
</child>
<child>
<object class="GtkImage">
<style>
<class name="large-icons"/>
</style>
<property name="paintable">resource:///images/org.gtk.Demo4.svg</property>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">File</property>
<property name="child">
<object class="GtkButton" id="source_file">
<child>
<object class="GtkDragSource">
<property name="propagation-phase">capture</property>
<signal name="prepare" handler="drag_prepare"/>
</object>
</child>
<property name="valign">center</property>
<property name="child">
<object class="GtkLabel">
<property name="label">—</property>
<property name="xalign">0</property>
<property name="ellipsize">start</property>
</object>
</property>
<signal name="clicked" handler="open_file_cb"/>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="copy_button">
<property name="valign">center</property>
<property name="label" translatable="yes">_Copy</property>
<signal name="clicked" handler="copy_button_clicked" object="source_stack"/>
<property name="use-underline">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparator">
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">12</property>
<child>
<object class="GtkDropTarget">
<property name="actions">copy</property>
<property name="formats">GdkTexture GdkPaintable GFile GdkRGBA gchararray</property>
<signal name="drop" handler="on_drop" object="dest_stack"/>
</object>
</child>
<child>
<object class="GtkButton" id="paste_button">
<property name="label" translatable="yes">_Paste</property>
<signal name="clicked" handler="paste_button_clicked" object="dest_stack"/>
<property name="use-underline">1</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="xalign">0</property>
<binding name="label">
<lookup name="visible-child-name" type="GtkStack">
dest_stack
</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkStack" id="dest_stack">
<property name="halign">end</property>
<property name="valign">center</property>
<child>
<object class="GtkStackPage">
<property name="name"></property>
<property name="child">
<object class="GtkLabel">
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">Text</property>
<property name="child">
<object class="GtkLabel">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="xalign">0</property>
<property name="ellipsize">end</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">Image</property>
<property name="child">
<object class="GtkImage">
<property name="halign">end</property>
<property name="valign">center</property>
<style>
<class name="large-icons"/>
</style>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">Color</property>
<property name="child">
<object class="GtkBox">
<property name="halign">end</property>
<property name="valign">center</property>
<child>
<object class="GtkColorSwatch">
<property name="accessible-role">img</property>
<property name="can-focus">0</property>
<property name="selectable">0</property>
<property name="has-menu">0</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">File</property>
<property name="child">
<object class="GtkLabel">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="xalign">0</property>
<property name="hexpand">1</property>
<property name="ellipsize">start</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
+3 -2
View File
@@ -24,7 +24,6 @@ do_cursors (GtkWidget *do_widget)
builder = gtk_builder_new_from_resource ("/cursors/cursors.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_signal_connect (window, "destroy",
@@ -35,7 +34,9 @@ do_cursors (GtkWidget *do_widget)
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
{
gtk_window_destroy (GTK_WINDOW (window));
}
return window;
}
File diff suppressed because it is too large Load Diff
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CURVE_TYPE_EDITOR (curve_editor_get_type ())
G_DECLARE_FINAL_TYPE (CurveEditor, curve_editor, CURVE, EDITOR, GtkWidget)
GtkWidget * curve_editor_new (void);
void curve_editor_set_edit (CurveEditor *self,
gboolean edit);
void curve_editor_set_path (CurveEditor *self,
GskPath *path);
GskPath * curve_editor_get_path (CurveEditor *self);
void curve_editor_set_stroke (CurveEditor *self,
GskStroke *stroke);
const GskStroke * curve_editor_get_stroke (CurveEditor *self);
void curve_editor_set_color (CurveEditor *self,
GdkRGBA *color);
const GdkRGBA * curve_editor_get_color (CurveEditor *self);
gboolean curve_editor_get_show_outline (CurveEditor *self);
void curve_editor_set_show_outline (CurveEditor *self,
gboolean show_outline);
G_END_DECLS
-272
View File
@@ -1,272 +0,0 @@
/* Path/Curve Editor
*
* This demo shows an elaborate curve editor that you would expect to find
* in a vector graphics editor. It is built on top of GTK's path APIs.
*/
#include <gtk/gtk.h>
#include "curve-editor.h"
static GskPath *
make_circle_path (void)
{
float w = 310;
float h = 310;
float cx = w / 2;
float cy = h / 2;
float pad = 20;
float r = (w - 2 * pad) / 2;
float k = 0.55228;
float kr = k * r;
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, cx, pad);
gsk_path_builder_curve_to (builder, cx + kr, pad,
w - pad, cy - kr,
w - pad, cy);
gsk_path_builder_curve_to (builder, w - pad, cy + kr,
cx + kr, h - pad,
cx, h - pad);
gsk_path_builder_curve_to (builder, cx - kr, h - pad,
pad, cy + kr,
pad, cy);
gsk_path_builder_curve_to (builder, pad, cy - kr,
cx - kr, pad,
cx, pad);
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static void
edit_changed (GtkToggleButton *button,
GParamSpec *pspec,
CurveEditor *editor)
{
curve_editor_set_edit (editor, gtk_toggle_button_get_active (button));
}
static void
reset (GtkButton *button,
CurveEditor *editor)
{
GskPath *path;
path = make_circle_path ();
curve_editor_set_path (editor, path);
gsk_path_unref (path);
}
static void
line_width_changed (GtkSpinButton *spin,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_width (stroke, gtk_spin_button_get_value (spin));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
cap_changed (GtkDropDown *combo,
GParamSpec *pspec,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_cap (stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
join_changed (GtkDropDown *combo,
GParamSpec *pspec,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_join (stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
color_changed (GtkColorChooser *chooser,
GParamSpec *pspec,
CurveEditor *editor)
{
GdkRGBA color;
gtk_color_chooser_get_rgba (chooser, &color);
curve_editor_set_color (editor, &color);
}
static void
stroke_toggled (GtkCheckButton *button,
CurveEditor *editor)
{
curve_editor_set_show_outline (editor, gtk_check_button_get_active (button));
gtk_widget_queue_draw (GTK_WIDGET (editor));
}
static void
limit_changed (GtkSpinButton *spin,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_miter_limit (stroke, gtk_spin_button_get_value (spin));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
dashes_changed (GtkEntry *entry,
GParamSpec *spec,
CurveEditor *editor)
{
const char *text;
char **split;
GArray *dash;
GskStroke *stroke;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
split = g_strsplit (text, " ", 0);
dash = g_array_new (FALSE, FALSE, sizeof (float));
for (int i = 0; split[i] != NULL; i++)
{
double d;
char *endp = 0;
d = g_strtod (split[i], &endp);
if (*endp == '\0')
g_array_append_vals (dash, (float[1]) { d }, 1);
}
g_strfreev (split);
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_dash (stroke, (const float *)dash->data, dash->len);
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
g_array_free (dash, TRUE);
}
GtkWidget *
do_curve (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
GtkWidget *demo;
GtkWidget *edit_toggle;
GtkWidget *reset_button;
GtkWidget *titlebar;
GtkWidget *stroke_toggle;
GtkWidget *line_width_spin;
GtkWidget *stroke_button;
GtkWidget *popover;
GtkWidget *grid;
GtkWidget *cap_combo;
GtkWidget *join_combo;
GtkWidget *color_button;
GtkWidget *limit_spin;
GtkWidget *dash_entry;
if (!window)
{
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), "Curve Editor");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
gtk_window_set_default_size (GTK_WINDOW (window), 310, 350);
edit_toggle = gtk_toggle_button_new ();
gtk_button_set_icon_name (GTK_BUTTON (edit_toggle), "document-edit-symbolic");
reset_button = gtk_button_new_from_icon_name ("edit-undo-symbolic");
stroke_button = gtk_menu_button_new ();
gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (stroke_button), "open-menu-symbolic");
popover = gtk_popover_new ();
gtk_menu_button_set_popover (GTK_MENU_BUTTON (stroke_button), popover);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_popover_set_child (GTK_POPOVER (popover), grid);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Color:"), 0, 0, 1, 1);
color_button = gtk_color_button_new_with_rgba (&(GdkRGBA){ 0., 0., 0., 1.});
gtk_grid_attach (GTK_GRID (grid), color_button, 1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, 1, 1, 1);
line_width_spin = gtk_spin_button_new_with_range (1, 20, 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 1);
gtk_grid_attach (GTK_GRID (grid), line_width_spin, 1, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, 2, 1, 1);
cap_combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
gtk_grid_attach (GTK_GRID (grid), cap_combo, 1, 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, 3, 1, 1);
join_combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs", NULL});
gtk_grid_attach (GTK_GRID (grid), join_combo, 1, 3, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, 4, 1, 1);
limit_spin = gtk_spin_button_new_with_range (0, 10, 1);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (limit_spin), 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (limit_spin), 4);
gtk_grid_attach (GTK_GRID (grid), limit_spin, 1, 4, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dashes:"), 0, 5, 1, 1);
dash_entry = gtk_entry_new ();
gtk_grid_attach (GTK_GRID (grid), dash_entry, 1, 5, 1, 1);
stroke_toggle = gtk_check_button_new_with_label ("Show outline");
gtk_grid_attach (GTK_GRID (grid), stroke_toggle, 1, 6, 1, 1);
titlebar = gtk_header_bar_new ();
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), edit_toggle);
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), reset_button);
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), stroke_button);
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
demo = curve_editor_new ();
g_signal_connect (stroke_toggle, "toggled", G_CALLBACK (stroke_toggled), demo);
g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
g_signal_connect (cap_combo, "notify::selected", G_CALLBACK (cap_changed), demo);
g_signal_connect (join_combo, "notify::selected", G_CALLBACK (join_changed), demo);
g_signal_connect (color_button, "notify::rgba", G_CALLBACK (color_changed), demo);
g_signal_connect (line_width_spin, "value-changed", G_CALLBACK (line_width_changed), demo);
g_signal_connect (limit_spin, "value-changed", G_CALLBACK (limit_changed), demo);
g_signal_connect (dash_entry, "notify::text", G_CALLBACK (dashes_changed), demo);
reset (NULL, CURVE_EDITOR (demo));
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 6);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &(GdkRGBA) { 1, 0, 0, 1 });
gtk_drop_down_set_selected (GTK_DROP_DOWN (cap_combo), GSK_LINE_CAP_ROUND);
gtk_editable_set_text (GTK_EDITABLE (dash_entry), "0 8");
gtk_window_set_child (GTK_WINDOW (window), demo);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+1 -12
View File
@@ -15,6 +15,7 @@
<file>demo.ui</file>
</gresource>
<gresource prefix="/clipboard">
<file>clipboard.ui</file>
<file>demoimage.c</file>
<file>demoimage.h</file>
</gresource>
@@ -249,10 +250,6 @@
<gresource prefix="/video-player">
<file>bbb.png</file>
</gresource>
<gresource prefix="/curve">
<file>curve-editor.c</file>
<file>curve-editor.h</file>
</gresource>
<gresource prefix="/sources">
<file>application_demo.c</file>
<file>assistant.c</file>
@@ -270,7 +267,6 @@
<file>css_pixbufs.c</file>
<file>css_shadows.c</file>
<file>cursors.c</file>
<file>curve.c</file>
<file>dialog.c</file>
<file>drawingarea.c</file>
<file>dropdown.c</file>
@@ -291,7 +287,6 @@
<file>gears.c</file>
<file>gestures.c</file>
<file>glarea.c</file>
<file>glyphs.c</file>
<file>gltransition.c</file>
<file>headerbar.c</file>
<file>hypertext.c</file>
@@ -330,9 +325,6 @@
<file>paintable_symbolic.c</file>
<file>panes.c</file>
<file>password_entry.c</file>
<file>path_fill.c</file>
<file>path_maze.c</file>
<file>path_text.c</file>
<file>peg_solitaire.c</file>
<file>pickers.c</file>
<file>printing.c</file>
@@ -417,9 +409,6 @@
<gresource prefix="/fontrendering">
<file>fontrendering.ui</file>
</gresource>
<gresource prefix="/path_text">
<file>path_text.ui</file>
</gresource>
<gresource prefix="/org/gtk/Demo4">
<file>icons/16x16/actions/application-exit.png</file>
<file>icons/16x16/actions/document-new.png</file>
File diff suppressed because it is too large Load Diff
-6
View File
@@ -17,7 +17,6 @@ demos = files([
'css_pixbufs.c',
'css_shadows.c',
'cursors.c',
'curve.c',
'dialog.c',
'drawingarea.c',
'dnd.c',
@@ -35,7 +34,6 @@ demos = files([
'gestures.c',
'glarea.c',
'gltransition.c',
'glyphs.c',
'headerbar.c',
'hypertext.c',
'iconscroll.c',
@@ -72,9 +70,6 @@ demos = files([
'paintable_symbolic.c',
'panes.c',
'password_entry.c',
'path_fill.c',
'path_maze.c',
'path_text.c',
'peg_solitaire.c',
'pickers.c',
'printing.c',
@@ -133,7 +128,6 @@ extra_demo_sources = files([
'script-names.c',
'unicode-names.c',
'suggestionentry.c',
'curve-editor.c',
])
if harfbuzz_dep.found() and pangoft_dep.found()
-348
View File
@@ -1,348 +0,0 @@
/* Path/Text Fill
*
* This demo shows how to use PangoCairo to draw text with more than
* just a single color.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#include "gsk/gskpathdashprivate.h"
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
struct _GtkPathPaintable
{
GObject parent_instance;
int width;
int height;
GskPath *path;
GdkPaintable *background;
};
struct _GtkPathPaintableClass
{
GObjectClass parent_class;
};
static int
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
else
return self->width;
}
static int
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
else
return self->height;
}
static void
gtk_path_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
#if 0
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
#else
GskStroke *stroke = gsk_stroke_new (2.0);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
#endif
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static GdkPaintableFlags
gtk_path_paintable_get_flags (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return gdk_paintable_get_flags (self->background);
else
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
}
static void
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->get_intrinsic_width = gtk_path_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_path_paintable_get_intrinsic_height;
iface->snapshot = gtk_path_paintable_snapshot;
iface->get_flags = gtk_path_paintable_get_flags;
}
/* When defining the GType, we need to implement the GdkPaintable interface */
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_path_paintable_paintable_init))
/* Here's the boilerplate for the GObject declaration.
* We don't need to do anything special here, because we keep no
* data of our own.
*/
static void
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
{
}
static void
gtk_path_paintable_init (GtkPathPaintable *self)
{
}
/* And finally, we add a simple constructor.
* It is declared in the header so that the other examples
* can use it.
*/
GdkPaintable *
gtk_path_paintable_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkPathPaintable *self;
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
self->path = path;
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
}
self->width = width;
self->height = height;
return GDK_PAINTABLE (self);
}
void
gtk_path_paintable_set_path (GtkPathPaintable *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
self->path = gsk_path_ref (path);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
}
static GskPath *
create_hexagon (GtkWidget *widget)
{
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 120, 0);
gsk_path_builder_line_to (builder, 360, 0);
gsk_path_builder_line_to (builder, 480, 208);
gsk_path_builder_line_to (builder, 360, 416);
gsk_path_builder_line_to (builder, 120, 416);
gsk_path_builder_line_to (builder, 0, 208);
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static GskPath *
create_path_from_text (GtkWidget *widget)
{
PangoLayout *layout;
PangoFontDescription *desc;
GskPathBuilder *builder;
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
builder = gsk_path_builder_new ();
gsk_path_builder_add_layout (builder, layout);
return gsk_path_builder_free_to_path (builder);
}
static gboolean
build_path (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
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_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
static gboolean
update_path (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer measure)
{
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
GskPathBuilder *builder;
GskPath *path;
graphene_point_t pos;
graphene_vec2_t tangent;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder,
measure,
#if 1
0.0, gsk_path_measure_get_length (measure));
#else
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
#endif
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (1);
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
builder = gsk_path_builder_new ();
gsk_path_dash (path, stroke, 0.2, build_path, builder);
gsk_path_unref (path);
gsk_path_measure_get_point (measure,
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
&pos,
&tangent);
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_close (builder);
path = gsk_path_builder_free_to_path (builder);
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
path);
gsk_path_unref (path);
return G_SOURCE_CONTINUE;
}
GtkWidget *
do_path_fill (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *picture;
GdkPaintable *paintable;
GtkMediaStream *stream;
GskPath *path;
graphene_rect_t bounds;
GskPathMeasure *measure;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_hexagon (window);
path = create_path_from_text (window);
gsk_path_get_bounds (path, &bounds);
paintable = gtk_path_paintable_new (path,
GDK_PAINTABLE (stream),
bounds.origin.x + bounds.size.width,
bounds.origin.y + bounds.size.height);
picture = gtk_picture_new_for_paintable (paintable);
measure = gsk_path_measure_new (path);
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
g_object_unref (paintable);
gtk_window_set_child (GTK_WINDOW (window), picture);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-339
View File
@@ -1,339 +0,0 @@
/* Path/Maze
* #Keywords: game, mouse
*
* This demo shows how to use a GskPath to create a maze and use
* gsk_path_measure_get_closest_point() to check the mouse stays
* on the path.
*
* It also shows off the performance of GskPath (or not) as this
* is a rather complex path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#define MAZE_GRID_SIZE 20
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
#define MAZE_WIDTH 31
#define MAZE_HEIGHT 21
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
struct _GtkMaze
{
GtkWidget parent_instance;
int width;
int height;
GskPath *path;
GskPathMeasure *measure;
GdkPaintable *background;
gboolean active;
};
struct _GtkMazeClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
static void
gtk_maze_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkMaze *self = GTK_MAZE (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*minimum = *natural = self->width;
else
*minimum = *natural = self->height;
}
static void
gtk_maze_snapshot (GtkWidget *widget,
GdkSnapshot *snapshot)
{
GtkMaze *self = GTK_MAZE (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskStroke *stroke;
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
if (self->active)
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static void
gtk_maze_dispose (GObject *object)
{
GtkMaze *self = GTK_MAZE (object);
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
if (self->background)
{
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
g_clear_object (&self->background);
}
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
}
static void
gtk_maze_class_init (GtkMazeClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_maze_dispose;
widget_class->measure = gtk_maze_measure;
widget_class->snapshot = gtk_maze_snapshot;
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkMaze *self)
{
if (!self->active)
return;
if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
return;
self->active = FALSE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkMaze *self)
{
if (!self->active)
{
self->active = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
gtk_maze_init (GtkMaze *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->active = TRUE;
}
static void
gtk_maze_set_path (GtkMaze *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
self->path = gsk_path_ref (path);
self->measure = gsk_path_measure_new (path);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
GtkWidget *
gtk_maze_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkMaze *self;
self = g_object_new (GTK_TYPE_MAZE, NULL);
gtk_maze_set_path (self, path);
gsk_path_unref (path);
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
}
self->width = width;
self->height = height;
return GTK_WIDGET (self);
}
static void
add_point_to_maze (GtkBitset *maze,
GskPathBuilder *builder,
guint x,
guint y)
{
gboolean set[4] = { };
guint dir;
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
while (TRUE)
{
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
if (set[0] && set[1] && set[2] && set[3])
return;
do
{
dir = g_random_int_range (0, 4);
}
while (set[dir]);
switch (dir)
{
case 0:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x - 1, y);
break;
case 1:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y - 1);
break;
case 2:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x + 1, y);
break;
case 3:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y + 1);
break;
default:
g_assert_not_reached ();
break;
}
}
}
static GskPath *
create_path_for_maze (GtkWidget *widget)
{
GskPathBuilder *builder;
GtkBitset *maze;
builder = gsk_path_builder_new ();
maze = gtk_bitset_new_empty ();
/* make sure the outer lines are unreachable:
* Set the full range, then remove the center again. */
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
/* Fill the maze */
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
/* Add start and stop lines */
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
gtk_bitset_unref (maze);
return gsk_path_builder_free_to_path (builder);
}
GtkWidget *
do_path_maze (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *maze;
GtkMediaStream *stream;
GskPath *path;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_path_for_maze (window);
maze = gtk_maze_new (path,
GDK_PAINTABLE (stream),
MAZE_WIDTH * MAZE_GRID_SIZE,
MAZE_HEIGHT * MAZE_GRID_SIZE);
gtk_window_set_child (GTK_WINDOW (window), maze);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-594
View File
@@ -1,594 +0,0 @@
/* Path/Curved Text
*
* This demo shows how to use GskPath to animate a path along another path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
#define POINT_SIZE 8
enum {
PROP_0,
PROP_TEXT,
PROP_EDITABLE,
N_PROPS
};
struct _GtkPathWidget
{
GtkWidget parent_instance;
char *text;
gboolean editable;
graphene_point_t points[4];
guint active_point;
float line_closest;
GskPath *line_path;
GskPathMeasure *line_measure;
GskPath *text_path;
GdkPaintable *background;
};
struct _GtkPathWidgetClass
{
GtkWidgetClass parent_class;
};
static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
static GskPath *
create_path_from_text (GtkWidget *widget,
const char *text)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *path;
PangoLayout *layout;
PangoFontDescription *desc;
GskPath *result;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
layout = gtk_widget_create_pango_layout (widget, text);
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
cairo_move_to (cr, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path_flat (cr);
result = gsk_path_new_from_cairo (path);
cairo_path_destroy (path);
g_object_unref (layout);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return result;
}
typedef struct
{
GskPathMeasure *measure;
GskPathBuilder *builder;
double scale;
} GtkPathTransform;
static void
gtk_path_transform_point (GskPathMeasure *measure,
const graphene_point_t *pt,
float scale,
graphene_point_t *res)
{
graphene_vec2_t tangent;
gsk_path_measure_get_point (measure, pt->x * scale, res, &tangent);
res->x -= pt->y * scale * graphene_vec2_get_y (&tangent);
res->y += pt->y * scale * graphene_vec2_get_x (&tangent);
}
static gboolean
gtk_path_transform_op (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer data)
{
GtkPathTransform *transform = data;
switch (op)
{
case GSK_PATH_MOVE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[0], transform->scale, &res);
gsk_path_builder_move_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_LINE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res);
gsk_path_builder_line_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_CURVE:
{
graphene_point_t res[3];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gtk_path_transform_point (transform->measure, &pts[3], transform->scale, &res[2]);
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t res[2];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
}
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (transform->builder);
break;
default:
g_assert_not_reached();
return FALSE;
}
return TRUE;
}
static GskPath *
gtk_path_transform (GskPathMeasure *measure,
GskPath *path)
{
GtkPathTransform transform = { measure, gsk_path_builder_new () };
graphene_rect_t bounds;
gsk_path_get_bounds (path, &bounds);
if (bounds.origin.x + bounds.size.width > 0)
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
else
transform.scale = 1.0f;
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
return gsk_path_builder_free_to_path (transform.builder);
}
static void
gtk_path_widget_clear_text_path (GtkPathWidget *self)
{
g_clear_pointer (&self->text_path, gsk_path_unref);
}
static void
gtk_path_widget_clear_paths (GtkPathWidget *self)
{
gtk_path_widget_clear_text_path (self);
g_clear_pointer (&self->line_path, gsk_path_unref);
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
}
static void
gtk_path_widget_create_text_path (GtkPathWidget *self)
{
GskPath *path;
gtk_path_widget_clear_text_path (self);
if (self->line_measure == NULL)
return;
path = create_path_from_text (GTK_WIDGET (self), self->text);
self->text_path = gtk_path_transform (self->line_measure, path);
gsk_path_unref (path);
}
static void
gtk_path_widget_create_paths (GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
GskPathBuilder *builder;
gtk_path_widget_clear_paths (self);
if (width <= 0 || height <= 0)
return;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder,
self->points[0].x * width, self->points[0].y * height);
gsk_path_builder_curve_to (builder,
self->points[1].x * width, self->points[1].y * height,
self->points[2].x * width, self->points[2].y * height,
self->points[3].x * width, self->points[3].y * height);
self->line_path = gsk_path_builder_free_to_path (builder);
self->line_measure = gsk_path_measure_new (self->line_path);
gtk_path_widget_create_text_path (self);
}
static void
gtk_path_widget_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
gtk_path_widget_create_paths (self);
}
static void
gtk_path_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskPath *path;
GskStroke *stroke;
gsize i;
/* frosted glass the background */
gtk_snapshot_push_blur (snapshot, 100);
gdk_paintable_snapshot (self->background, snapshot, width, height);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.5 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
/* draw the text */
if (self->text_path)
{
gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
gdk_paintable_snapshot (self->background, snapshot, width, height);
/* ... with an emboss effect */
stroke = gsk_stroke_new (2.0);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gsk_stroke_free (stroke);
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (snapshot);
}
if (self->editable && self->line_path)
{
/* draw the control line */
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
}
if (self->line_closest >= 0)
{
GskPathBuilder *builder;
graphene_point_t closest;
builder = gsk_path_builder_new ();
gsk_path_measure_get_point (self->line_measure, self->line_closest, &closest, NULL);
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
if (self->editable && self->line_path)
{
GskPathBuilder *builder;
/* draw the points */
builder = gsk_path_builder_new ();
for (i = 0; i < 4; i++)
{
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
}
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
}
static void
gtk_path_widget_set_text (GtkPathWidget *self,
const char *text)
{
if (g_strcmp0 (self->text, text) == 0)
return;
g_free (self->text);
self->text = g_strdup (text);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
}
static void
gtk_path_widget_set_editable (GtkPathWidget *self,
gboolean editable)
{
if (self->editable == editable)
return;
self->editable = editable;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
}
static void
gtk_path_widget_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
gtk_path_widget_set_text (self, g_value_get_string (value));
break;
case PROP_EDITABLE:
gtk_path_widget_set_editable (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
g_value_set_string (value, self->text);
break;
case PROP_EDITABLE:
g_value_set_boolean (value, self->editable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_dispose (GObject *object)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
gtk_path_widget_clear_paths (self);
G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
}
static void
gtk_path_widget_class_init (GtkPathWidgetClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_path_widget_dispose;
object_class->set_property = gtk_path_widget_set_property;
object_class->get_property = gtk_path_widget_get_property;
widget_class->size_allocate = gtk_path_widget_allocate;
widget_class->snapshot = gtk_path_widget_snapshot;
properties[PROP_TEXT] =
g_param_spec_string ("text",
"text",
"Text transformed along a path",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_EDITABLE] =
g_param_spec_boolean ("editable",
"editable",
"If the path can be edited by the user",
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkPathWidget *self)
{
graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
gsize i;
for (i = 0; i < 4; i++)
{
if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
{
self->active_point = i;
break;
}
}
if (i == 4)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
return;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
drag_update (GtkGestureDrag *drag,
double offset_x,
double offset_y,
GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
double start_x, start_y;
gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
(start_y + offset_y) / height);
self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkPathWidget *self)
{
gsk_path_measure_get_closest_point_full (self->line_measure,
&GRAPHENE_POINT_INIT (x, y),
INFINITY,
NULL, NULL,
&self->line_closest,
NULL);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkPathWidget *self)
{
self->line_closest = -1;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
gtk_path_widget_init (GtkPathWidget *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->line_closest = -1;
self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
gtk_path_widget_set_text (self, "It's almost working");
}
GtkWidget *
gtk_path_widget_new (void)
{
GtkPathWidget *self;
self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
return GTK_WIDGET (self);
}
GtkWidget *
do_path_text (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkBuilder *builder;
g_type_ensure (GTK_TYPE_PATH_WIDGET);
builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-38
View File
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Text along a Path</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<child type="end">
<object class="GtkToggleButton" id="edit-toggle">
<property name="icon-name">document-edit-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer">
<property name="reveal-child" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<child>
<object class="GtkEntry" id="text">
<property name="text">Through the looking glass</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkPathWidget" id="view">
<property name="editable" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<property name="text" bind-source="text" bind-property="text" bind-flags="sync-create"></property>
<property name="hexpand">true</property>
<property name="vexpand">true</property>
</object>
</child>
</object>
</child>
</object>
</interface>
+8
View File
@@ -16,6 +16,7 @@ SYNOPSIS
| **gtk4-builder-tool** enumerate <FILE>
| **gtk4-builder-tool** simplify [OPTIONS...] <FILE>
| **gtk4-builder-tool** preview [OPTIONS...] <FILE>
| **gtk4-builder-tool** precompile [OPTIONS...] <FILE>
DESCRIPTION
-----------
@@ -83,3 +84,10 @@ to do manual fixups after the initial conversion.
``--3to4``
Transform a GTK 3 UI definition file to the equivalent GTK 4 definitions.
Precompilation
^^^^^^^^^^^^^^
The ``precompile`` command creates a more compact, and faster to load compiled
form of the ui file that is understood by GtkBuilder. The output is written
to a file with the extension ``.precompiled``.
+1 -20
View File
@@ -49,7 +49,7 @@
typedef struct _Deserializer Deserializer;
struct _Deserializer
struct _Deserializer
{
const char * mime_type; /* interned */
GType type;
@@ -934,25 +934,6 @@ init (void)
formats = gdk_pixbuf_get_formats ();
/* Make sure png comes first */
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char *name;
name = gdk_pixbuf_format_get_name (fmt);
if (g_str_equal (name, "png"))
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
g_free (name);
break;
}
g_free (name);
}
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
+26 -12
View File
@@ -718,19 +718,33 @@ gdk_content_formats_builder_to_formats (GdkContentFormatsBuilder *builder)
g_return_val_if_fail (builder != NULL, NULL);
gtypes = g_new (GType, builder->n_gtypes + 1);
i = builder->n_gtypes;
gtypes[i--] = G_TYPE_INVALID;
/* add backwards because most important type is last in the list */
for (l = builder->gtypes; l; l = l->next)
gtypes[i--] = GPOINTER_TO_SIZE (l->data);
if (builder->n_gtypes > 0)
{
gtypes = g_new (GType, builder->n_gtypes + 1);
i = builder->n_gtypes;
gtypes[i--] = G_TYPE_INVALID;
/* add backwards because most important type is last in the list */
for (l = builder->gtypes; l; l = l->next)
gtypes[i--] = GPOINTER_TO_SIZE (l->data);
}
else
{
gtypes = NULL;
}
mime_types = g_new (const char *, builder->n_mime_types + 1);
i = builder->n_mime_types;
mime_types[i--] = NULL;
/* add backwards because most important type is last in the list */
for (l = builder->mime_types; l; l = l->next)
mime_types[i--] = l->data;
if (builder->n_mime_types > 0)
{
mime_types = g_new (const char *, builder->n_mime_types + 1);
i = builder->n_mime_types;
mime_types[i--] = NULL;
/* add backwards because most important type is last in the list */
for (l = builder->mime_types; l; l = l->next)
mime_types[i--] = l->data;
}
else
{
mime_types = NULL;
}
result = gdk_content_formats_new_take (gtypes, builder->n_gtypes,
mime_types, builder->n_mime_types);
+6 -25
View File
@@ -54,7 +54,7 @@
typedef struct _Serializer Serializer;
struct _Serializer
struct _Serializer
{
const char * mime_type; /* interned */
GType type;
@@ -446,7 +446,7 @@ lookup_serializer (const char *mime_type,
serializer->type == type)
return serializer;
}
return NULL;
}
@@ -630,7 +630,7 @@ pixbuf_serializer (GdkContentSerializer *serializer)
const GValue *value;
GdkPixbuf *pixbuf;
const char *name;
name = gdk_content_serializer_get_user_data (serializer);
value = gdk_content_serializer_get_value (serializer);
@@ -651,7 +651,7 @@ pixbuf_serializer (GdkContentSerializer *serializer)
gdk_pixbuf_save_to_stream_async (pixbuf,
gdk_content_serializer_get_output_stream (serializer),
name,
gdk_content_serializer_get_cancellable (serializer),
gdk_content_serializer_get_cancellable (serializer),
pixbuf_serializer_finish,
serializer,
g_str_equal (name, "png") ? "compression" : NULL, "2",
@@ -823,7 +823,7 @@ file_uri_serializer (GdkContentSerializer *serializer)
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
{
GSList *l;
for (l = g_value_get_boxed (value); l; l = l->next)
{
uri = g_file_get_uri (l->data);
@@ -867,7 +867,7 @@ file_text_serializer (GdkContentSerializer *serializer)
{
GString *str;
GSList *l;
str = g_string_new (NULL);
for (l = g_value_get_boxed (value); l; l = l->next)
@@ -966,25 +966,6 @@ init (void)
formats = gdk_pixbuf_get_formats ();
/* Make sure png comes first */
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
char *name;
name = gdk_pixbuf_format_get_name (fmt);
if (g_str_equal (name, "png"))
{
formats = g_slist_delete_link (formats, f);
formats = g_slist_prepend (formats, fmt);
g_free (name);
break;
}
g_free (name);
}
for (f = formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
+4
View File
@@ -132,6 +132,8 @@ gdk_device_class_init (GdkDeviceClass *klass)
* GdkDevice:source: (attributes org.gtk.Property.get=gdk_device_get_source)
*
* Source type for the device.
*
* Deprecated: 4.6: Use GdkDeviceTool:tool-type instead
*/
device_props[PROP_SOURCE] =
g_param_spec_enum ("source",
@@ -596,6 +598,8 @@ gdk_device_get_has_cursor (GdkDevice *device)
* Determines the type of the device.
*
* Returns: a `GdkInputSource`
*
* Deprecated: 4.6: Use gdk_device_tool_get_tool_type() instead
*/
GdkInputSource
gdk_device_get_source (GdkDevice *device)
+1 -1
View File
@@ -92,7 +92,7 @@ GDK_AVAILABLE_IN_ALL
GdkDisplay * gdk_device_get_display (GdkDevice *device);
GDK_AVAILABLE_IN_ALL
GdkSeat * gdk_device_get_seat (GdkDevice *device);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_6_FOR(gdk_device_tool_get_tool_type)
GdkDeviceTool * gdk_device_get_device_tool (GdkDevice *device);
GDK_AVAILABLE_IN_ALL
+1
View File
@@ -2,3 +2,4 @@ BOOLEAN:BOXED
BOOLEAN:OBJECT
BOOLEAN:POINTER
VOID:POINTER,POINTER,BOOLEAN,BOOLEAN
VOID:INT,INT
+4 -1
View File
@@ -605,11 +605,14 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
0,
NULL,
NULL,
NULL,
_gdk_marshal_VOID__INT_INT,
G_TYPE_NONE,
2,
G_TYPE_INT,
G_TYPE_INT);
g_signal_set_va_marshaller (signals[LAYOUT],
G_OBJECT_CLASS_TYPE (object_class),
_gdk_marshal_VOID__INT_INTv);
/**
* GdkSurface::render:
-4
View File
@@ -89,10 +89,6 @@ GDK_AVAILABLE_IN_ALL
void gdk_texture_download (GdkTexture *texture,
guchar *data,
gsize stride);
GDK_AVAILABLE_IN_4_6
void gdk_texture_download_float (GdkTexture *texture,
float *data,
gsize stride);
GDK_AVAILABLE_IN_ALL
gboolean gdk_texture_save_to_png (GdkTexture *texture,
const char *filename);
+8 -1
View File
@@ -240,7 +240,7 @@ static const FormatData format_data[] = {
[GDK_MEMORY_A8R8G8B8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
[GDK_MEMORY_R8G8B8A8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
[GDK_MEMORY_A8B8G8R8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
[GDK_MEMORY_R8G8B8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
[GDK_MEMORY_R8G8B8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
[GDK_MEMORY_B8G8R8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
[GDK_MEMORY_R16G16B16] = { GDK_MEMORY_R16G16B16, 16, 3, SAMPLEFORMAT_UINT, 0 },
[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] = { GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_ASSOCALPHA },
@@ -376,6 +376,13 @@ gdk_load_tiff (GBytes *input_bytes,
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
tif = tiff_open_read (input_bytes);
if (!tif)
{
g_set_error_literal (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE,
_("Could not load TIFF data"));
return NULL;
}
TIFFSetDirectory (tif, 0);
+26 -2
View File
@@ -76,11 +76,30 @@ print_atoms (GdkX11Clipboard *cb,
});
}
static void
gdk_x11_clipboard_default_output_closed (GObject *stream,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, &error))
{
GDK_NOTE (CLIPBOARD,
g_printerr ("-------: failed to close stream: %s\n",
error->message));
g_error_free (error);
}
g_object_unref (stream);
}
static void
gdk_x11_clipboard_default_output_done (GObject *clipboard,
GAsyncResult *result,
gpointer user_data)
{
GOutputStream *stream = user_data;
GError *error = NULL;
if (!gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, &error))
@@ -90,6 +109,12 @@ gdk_x11_clipboard_default_output_done (GObject *clipboard,
GDK_X11_CLIPBOARD (clipboard)->selection, error->message));
g_error_free (error);
}
g_output_stream_close_async (stream,
G_PRIORITY_DEFAULT,
NULL,
gdk_x11_clipboard_default_output_closed,
NULL);
}
static void
@@ -103,8 +128,7 @@ gdk_x11_clipboard_default_output_handler (GOutputStream *stream,
G_PRIORITY_DEFAULT,
NULL,
gdk_x11_clipboard_default_output_done,
NULL);
g_object_unref (stream);
stream);
}
static GInputStream *
+26 -2
View File
@@ -1612,11 +1612,30 @@ gdk_x11_drag_set_hotspot (GdkDrag *drag,
}
}
static void
gdk_x11_drag_default_output_closed (GObject *stream,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, &error))
{
GDK_NOTE (DND,
g_printerr ("failed to close stream: %s\n",
error->message));
g_error_free (error);
}
g_object_unref (stream);
}
static void
gdk_x11_drag_default_output_done (GObject *drag,
GAsyncResult *result,
gpointer user_data)
{
GOutputStream *stream = user_data;
GError *error = NULL;
if (!gdk_drag_write_finish (GDK_DRAG (drag), result, &error))
@@ -1624,6 +1643,12 @@ gdk_x11_drag_default_output_done (GObject *drag,
GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_printerr ("failed to write stream: %s\n", error->message));
g_error_free (error);
}
g_output_stream_close_async (stream,
G_PRIORITY_DEFAULT,
NULL,
gdk_x11_drag_default_output_closed,
NULL);
}
static void
@@ -1637,8 +1662,7 @@ gdk_x11_drag_default_output_handler (GOutputStream *stream,
G_PRIORITY_DEFAULT,
NULL,
gdk_x11_drag_default_output_done,
NULL);
g_object_unref (stream);
stream);
}
static gboolean
+19 -60
View File
@@ -57,7 +57,8 @@ struct _GdkX11SelectionOutputStreamPrivate {
GTask *pending_task;
guint incr : 1;
guint delete_pending : 1;
guint sent_end_of_stream : 1;
guint delete_pending : 1; /* owns a reference */
};
struct _GdkX11PendingSelectionNotify
@@ -176,12 +177,16 @@ gdk_x11_selection_output_stream_needs_flush_unlocked (GdkX11SelectionOutputStrea
{
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
if (priv->data->len == 0 && priv->notify == NULL)
if (priv->sent_end_of_stream)
return FALSE;
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)) ||
g_output_stream_is_closed (G_OUTPUT_STREAM (stream)))
return TRUE;
if (priv->data->len == 0 && priv->notify == NULL)
return FALSE;
if (priv->flush_requested)
return TRUE;
@@ -284,6 +289,8 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
g_byte_array_remove_range (priv->data, 0, n_elements * element_size);
if (priv->data->len < element_size)
priv->flush_requested = FALSE;
if (!priv->incr || n_elements == 0)
priv->sent_end_of_stream = TRUE;
}
if (priv->notify)
@@ -292,6 +299,7 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
priv->notify = NULL;
}
g_object_ref (stream);
priv->delete_pending = TRUE;
g_cond_broadcast (&priv->cond);
g_mutex_unlock (&priv->mutex);
@@ -494,60 +502,6 @@ gdk_x11_selection_output_stream_flush_finish (GOutputStream *stream,
return g_task_propagate_boolean (G_TASK (result), error);
}
static gboolean
gdk_x11_selection_output_stream_invoke_close (gpointer stream)
{
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, stream);
g_signal_handlers_disconnect_by_func (priv->display,
gdk_x11_selection_output_stream_xevent,
stream);
g_object_unref (stream);
return G_SOURCE_REMOVE;
}
static gboolean
gdk_x11_selection_output_stream_close (GOutputStream *stream,
GCancellable *cancellable,
GError **error)
{
g_main_context_invoke (NULL, gdk_x11_selection_output_stream_invoke_close, g_object_ref (stream));
return TRUE;
}
static void
gdk_x11_selection_output_stream_close_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (stream, cancellable, callback, user_data);
g_task_set_source_tag (task, gdk_x11_selection_output_stream_close_async);
g_task_set_priority (task, io_priority);
gdk_x11_selection_output_stream_invoke_close (stream);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static gboolean
gdk_x11_selection_output_stream_close_finish (GOutputStream *stream,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
g_return_val_if_fail (g_async_result_is_tagged (result, gdk_x11_selection_output_stream_close_async), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
gdk_x11_selection_output_stream_finalize (GObject *object)
{
@@ -557,6 +511,13 @@ gdk_x11_selection_output_stream_finalize (GObject *object)
/* not sending a notify is terrible */
g_assert (priv->notify == NULL);
GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: finalizing\n",
priv->selection, priv->target));
GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, stream);
g_signal_handlers_disconnect_by_func (priv->display,
gdk_x11_selection_output_stream_xevent,
stream);
g_byte_array_unref (priv->data);
g_cond_clear (&priv->cond);
g_mutex_clear (&priv->mutex);
@@ -579,14 +540,11 @@ gdk_x11_selection_output_stream_class_init (GdkX11SelectionOutputStreamClass *kl
output_stream_class->write_fn = gdk_x11_selection_output_stream_write;
output_stream_class->flush = gdk_x11_selection_output_stream_flush;
output_stream_class->close_fn = gdk_x11_selection_output_stream_close;
output_stream_class->write_async = gdk_x11_selection_output_stream_write_async;
output_stream_class->write_finish = gdk_x11_selection_output_stream_write_finish;
output_stream_class->flush_async = gdk_x11_selection_output_stream_flush_async;
output_stream_class->flush_finish = gdk_x11_selection_output_stream_flush_finish;
output_stream_class->close_async = gdk_x11_selection_output_stream_close_async;
output_stream_class->close_finish = gdk_x11_selection_output_stream_close_finish;
}
static void
@@ -628,6 +586,7 @@ gdk_x11_selection_output_stream_xevent (GdkDisplay *display,
if (gdk_x11_selection_output_stream_needs_flush (stream) &&
gdk_x11_selection_output_stream_can_flush (stream))
gdk_x11_selection_output_stream_perform_flush (stream);
g_object_unref (stream); /* from unsetting the delete_pending */
return FALSE;
default:
-4
View File
@@ -269,8 +269,6 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
@@ -857,8 +855,6 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
break; /* Fallback */
}
+66 -20
View File
@@ -245,6 +245,47 @@ gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
self->corner[1].height + self->corner[2].height);
}
static inline gboolean G_GNUC_PURE
node_supports_2d_transform (const GskRenderNode *node)
{
switch ((int)gsk_render_node_get_node_type (node))
{
case GSK_COLOR_NODE:
case GSK_OPACITY_NODE:
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXTURE_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_DEBUG_NODE:
case GSK_TEXT_NODE:
case GSK_CAIRO_NODE:
case GSK_BLEND_NODE:
case GSK_BLUR_NODE:
return TRUE;
case GSK_SHADOW_NODE:
return node_supports_2d_transform (gsk_shadow_node_get_child (node));
case GSK_TRANSFORM_NODE:
return node_supports_2d_transform (gsk_transform_node_get_child (node));
case GSK_CONTAINER_NODE:
for (guint i = 0, p = gsk_container_node_get_n_children (node); i < p; i++)
{
if (!node_supports_2d_transform (gsk_container_node_get_child (node, i)))
return FALSE;
}
return TRUE;
default:
return FALSE;
}
}
static inline gboolean G_GNUC_PURE
node_supports_transform (const GskRenderNode *node)
{
@@ -257,24 +298,26 @@ node_supports_transform (const GskRenderNode *node)
switch ((int)gsk_render_node_get_node_type (node))
{
case GSK_COLOR_NODE:
case GSK_OPACITY_NODE:
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXTURE_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_LINEAR_GRADIENT_NODE:
case GSK_DEBUG_NODE:
case GSK_TEXT_NODE:
return TRUE;
case GSK_COLOR_NODE:
case GSK_OPACITY_NODE:
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXTURE_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_DEBUG_NODE:
case GSK_TEXT_NODE:
case GSK_CAIRO_NODE:
case GSK_BLEND_NODE:
case GSK_BLUR_NODE:
return TRUE;
case GSK_SHADOW_NODE:
return node_supports_transform (gsk_shadow_node_get_child (node));
case GSK_SHADOW_NODE:
return node_supports_transform (gsk_shadow_node_get_child (node));
case GSK_TRANSFORM_NODE:
return node_supports_transform (gsk_transform_node_get_child (node));
case GSK_TRANSFORM_NODE:
return node_supports_transform (gsk_transform_node_get_child (node));
default:
return FALSE;
default:
return FALSE;
}
}
@@ -2017,6 +2060,14 @@ gsk_gl_render_job_visit_transform_node (GskGLRenderJob *job,
break;
case GSK_TRANSFORM_CATEGORY_2D:
if (node_supports_2d_transform (child))
{
gsk_gl_render_job_push_modelview (job, transform);
gsk_gl_render_job_visit_node (job, child);
gsk_gl_render_job_pop_modelview (job);
return;
}
G_GNUC_FALLTHROUGH;
case GSK_TRANSFORM_CATEGORY_3D:
case GSK_TRANSFORM_CATEGORY_ANY:
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
@@ -3767,11 +3818,6 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_as_fallback (job, node);
break;
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
gsk_gl_render_job_visit_as_fallback (job, node);
break;
case GSK_NOT_A_RENDER_NODE:
default:
g_assert_not_reached ();
-2
View File
@@ -22,8 +22,6 @@
#ifndef __GI_SCANNER__
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
+1 -5
View File
@@ -21,15 +21,11 @@
#define __GSK_H_INSIDE__
#include <gsk/gskenums.h>
#include <gsk/gskglshader.h>
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
#include <gsk/gskpathmeasure.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>
#include <gsk/gskroundedrect.h>
#include <gsk/gskstroke.h>
#include <gsk/gsktransform.h>
#include <gsk/gskglshader.h>
#include <gsk/gskcairorenderer.h>
-80
View File
@@ -1,80 +0,0 @@
#include "config.h"
#include "gskboundingboxprivate.h"
GskBoundingBox *
gsk_bounding_box_init (GskBoundingBox *self,
const graphene_point_t *a,
const graphene_point_t *b)
{
self->min.x = MIN (a->x, b->x);
self->min.y = MIN (a->y, b->y);
self->max.x = MAX (a->x, b->x);
self->max.y = MAX (a->y, b->y);
return self;
}
GskBoundingBox *
gsk_bounding_box_init_copy (GskBoundingBox *self,
const GskBoundingBox *src)
{
self->min = src->min;
self->max = src->max;
return self;
}
GskBoundingBox *
gsk_bounding_box_init_from_rect (GskBoundingBox *self,
const graphene_rect_t *bounds)
{
self->min = bounds->origin;
self->max.x = bounds->origin.x + bounds->size.width;
self->max.y = bounds->origin.y + bounds->size.height;
return self;
}
void
gsk_bounding_box_expand (GskBoundingBox *self,
const graphene_point_t *p)
{
self->min.x = MIN (self->min.x, p->x);
self->min.y = MIN (self->min.y, p->y);
self->max.x = MAX (self->max.x, p->x);
self->max.y = MAX (self->max.y, p->y);
}
graphene_rect_t *
gsk_bounding_box_to_rect (const GskBoundingBox *self,
graphene_rect_t *rect)
{
rect->origin = self->min;
rect->size.width = self->max.x - self->min.x;
rect->size.height = self->max.y - self->min.y;
return rect;
}
gboolean
gsk_bounding_box_contains_point (const GskBoundingBox *self,
const graphene_point_t *p)
{
return self->min.x <= p->x && p->x <= self->max.x &&
self->min.y <= p->y && p->y <= self->max.y;
}
gboolean
gsk_bounding_box_intersection (const GskBoundingBox *a,
const GskBoundingBox *b,
GskBoundingBox *res)
{
graphene_point_t min, max;
min.x = MAX (a->min.x, b->min.x);
min.y = MAX (a->min.y, b->min.y);
max.x = MIN (a->max.x, b->max.x);
max.y = MIN (a->max.y, b->max.y);
if (res)
gsk_bounding_box_init (res, &min, &max);
return min.x <= max.x && min.y <= max.y;
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
typedef struct _GskBoundingBox GskBoundingBox;
struct _GskBoundingBox {
graphene_point_t min;
graphene_point_t max;
};
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init (GskBoundingBox *self,
const graphene_point_t *a,
const graphene_point_t *b);
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init_copy (GskBoundingBox *self,
const GskBoundingBox *src);
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init_from_rect (GskBoundingBox *self,
const graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
void gsk_bounding_box_expand (GskBoundingBox *self,
const graphene_point_t *p);
GDK_AVAILABLE_IN_ALL
graphene_rect_t *gsk_bounding_box_to_rect (const GskBoundingBox *self,
graphene_rect_t *rect);
GDK_AVAILABLE_IN_ALL
gboolean gsk_bounding_box_contains_point (const GskBoundingBox *self,
const graphene_point_t *p);
GDK_AVAILABLE_IN_ALL
gboolean gsk_bounding_box_intersection (const GskBoundingBox *a,
const GskBoundingBox *b,
GskBoundingBox *res);
G_END_DECLS
-2099
View File
File diff suppressed because it is too large Load Diff
-132
View File
@@ -1,132 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CONTOUR_PRIVATE_H__
#define __GSK_CONTOUR_PRIVATE_H__
#include <gskpath.h>
#include "gskpathopprivate.h"
G_BEGIN_DECLS
/* A path is flat if it contains no cubic or conic segments.
* A path is closed if all its contours end with a GSK_PATH_CLOSE
* operation.
*/
typedef enum
{
GSK_PATH_FLAT,
GSK_PATH_CLOSED
} GskPathFlags;
typedef struct _GskContour GskContour;
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
float radius,
float start_angle,
float end_angle);
GskContour * gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset);
void gsk_contour_copy (GskContour * dest,
const GskContour *src);
GskContour * gsk_contour_dup (const GskContour *src);
GskContour * gsk_contour_reverse (const GskContour *src);
gsize gsk_contour_get_size (const GskContour *self);
GskPathFlags gsk_contour_get_flags (const GskContour *self);
void gsk_contour_print (const GskContour *self,
GString *string);
gboolean gsk_contour_get_bounds (const GskContour *self,
graphene_rect_t *bounds);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
gboolean gsk_contour_foreach (const GskContour *self,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
graphene_point_t *end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
float gsk_contour_get_curvature (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *center);
gboolean gsk_contour_get_closest_point (const GskContour *self,
gpointer measure_data,
float tolerance,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
int gsk_contour_get_winding (const GskContour *self,
gpointer measure_data,
const graphene_point_t *point,
gboolean *on_edge);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end);
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
const GskStroke *stroke,
graphene_rect_t *bounds);
void gsk_contour_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_default_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
void gsk_contour_default_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
gboolean gsk_contour_is_convex (const GskContour *contour);
G_END_DECLS
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
-286
View File
@@ -1,286 +0,0 @@
#include "config.h"
#include "gskconvexityprivate.h"
#include "gskcontourprivate.h"
typedef enum
{
GSK_DIR_CHANGE_UNKNOWN,
GSK_DIR_CHANGE_LEFT,
GSK_DIR_CHANGE_RIGHT,
GSK_DIR_CHANGE_STRAIGHT,
GSK_DIR_CHANGE_REVERSE,
GSK_DIR_CHANGE_INVALID,
} GskDirChange;
typedef enum
{
GSK_PATH_DIRECTION_UNKNOWN,
GSK_PATH_DIRECTION_CLOCKWISE,
GSK_PATH_DIRECTION_COUNTERCLOCKWISE,
} GskPathDirection;
typedef struct _GskConvexityChecker GskConvexityChecker;
struct _GskConvexityChecker
{
graphene_point_t first_point;
graphene_vec2_t first_vec;
graphene_point_t last_point;
graphene_vec2_t last_vec;
GskDirChange expected_direction;
GskPathDirection first_direction;
int reversals;
gboolean finite;
GskConvexity convexity;
int xsign;
int ysign;
int dx;
int dy;
};
static float
cross_product (graphene_vec2_t *a,
graphene_vec2_t *b)
{
float fa[2];
float fb[2];
graphene_vec2_to_float (a, fa);
graphene_vec2_to_float (b, fb);
return fa[0] * fb[1] - fa[1] * fb[0];
}
static GskDirChange
direction_change (GskConvexityChecker *c,
graphene_vec2_t *v)
{
float cross = cross_product (&(c->last_vec), v);
if (!finite (cross))
return GSK_DIR_CHANGE_UNKNOWN;
if (cross == 0)
return graphene_vec2_dot (&(c->last_vec), v) < 0
? GSK_DIR_CHANGE_REVERSE
: GSK_DIR_CHANGE_STRAIGHT;
return cross > 0
? GSK_DIR_CHANGE_RIGHT
: GSK_DIR_CHANGE_LEFT;
}
static gboolean
add_vec (GskConvexityChecker *c,
graphene_vec2_t *v)
{
GskDirChange dir;
int sign;
dir = direction_change (c, v);
switch (dir)
{
case GSK_DIR_CHANGE_LEFT:
case GSK_DIR_CHANGE_RIGHT:
if (c->expected_direction == GSK_DIR_CHANGE_INVALID)
{
c->expected_direction = dir;
c->first_direction = (dir == GSK_DIR_CHANGE_RIGHT)
? GSK_PATH_DIRECTION_CLOCKWISE
: GSK_PATH_DIRECTION_COUNTERCLOCKWISE;
}
else if (c->expected_direction != dir)
{
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
graphene_vec2_init_from_vec2 (&c->last_vec, v);
break;
case GSK_DIR_CHANGE_STRAIGHT:
break;
case GSK_DIR_CHANGE_REVERSE:
graphene_vec2_init_from_vec2 (&c->last_vec, v);
c->reversals++;
if (c->reversals > 2)
c->convexity = GSK_CONVEXITY_CONCAVE;
return c->reversals < 3;
case GSK_DIR_CHANGE_UNKNOWN:
c->finite = FALSE;
return FALSE;
case GSK_DIR_CHANGE_INVALID:
default:
g_assert_not_reached ();
}
if (graphene_vec2_get_x (v) > 0)
sign = 1;
else if (graphene_vec2_get_x (v) < 0)
sign = -1;
else
sign = 0;
if (sign != 0)
{
if (c->xsign != 42)
{
if (c->xsign != sign)
c->dx++;
if (c->dx > 2)
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
}
c->xsign = sign;
}
if (graphene_vec2_get_y (v) > 0)
sign = 1;
else if (graphene_vec2_get_y (v) < 0)
sign = -1;
else
sign = 0;
if (sign != 0)
{
if (c->ysign != 42)
{
if (c->ysign != sign)
c->dy++;
if (c->dy > 2)
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
}
c->ysign = sign;
}
return TRUE;
}
static void
gsk_convexity_checker_init (GskConvexityChecker *c)
{
c->first_point = GRAPHENE_POINT_INIT(0,0);
c->last_point = GRAPHENE_POINT_INIT(0,0);
graphene_vec2_init (&c->first_vec, 0, 0);
graphene_vec2_init (&c->last_vec, 0, 0);
c->expected_direction = GSK_DIR_CHANGE_INVALID;
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
c->reversals = 0;
c->finite = TRUE;
c->convexity = GSK_CONVEXITY_UNKNOWN;
c->xsign = 42;
c->ysign = 42;
c->dx = 0;
c->dy = 0;
}
static void
gsk_convexity_checker_move (GskConvexityChecker *c,
const graphene_point_t *p)
{
c->first_point = c->last_point = *p;
c->expected_direction = GSK_DIR_CHANGE_INVALID;
c->convexity = GSK_CONVEXITY_CONVEX;
}
static gboolean
gsk_convexity_checker_add_point (GskConvexityChecker *c,
const graphene_point_t *p)
{
graphene_vec2_t v;
if (graphene_point_equal (&c->last_point, p))
return TRUE;
graphene_vec2_init (&v,
p->x - c->last_point.x,
p->y - c->last_point.y);
if (graphene_point_equal (&c->first_point, &c->last_point) &&
c->expected_direction == GSK_DIR_CHANGE_INVALID)
{
graphene_vec2_init_from_vec2 (&c->last_vec, &v);
graphene_vec2_init_from_vec2 (&c->first_vec, &v);
}
else if (!add_vec (c, &v))
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
c->last_point = *p;
return TRUE;
}
static gboolean
gsk_convexity_checker_close (GskConvexityChecker *c)
{
if (!(gsk_convexity_checker_add_point (c, &c->first_point) &&
add_vec (c, &c->first_vec)))
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
return TRUE;
}
static gboolean
convex_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskConvexityChecker *c = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_convexity_checker_move (c, &pts[0]);
break;
case GSK_PATH_CLOSE:
if (!gsk_convexity_checker_close (c))
return FALSE;
break;
case GSK_PATH_LINE:
if (!gsk_convexity_checker_add_point (c, &pts[1]))
return FALSE;
break;
case GSK_PATH_CURVE:
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
!gsk_convexity_checker_add_point (c, &pts[2]) ||
!gsk_convexity_checker_add_point (c, &pts[3]))
return FALSE;
break;
case GSK_PATH_CONIC:
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
!gsk_convexity_checker_add_point (c, &pts[3]))
return FALSE;
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
GskConvexity
gsk_contour_compute_convexity (const GskContour *contour)
{
GskConvexityChecker c;
gsk_convexity_checker_init (&c);
gsk_contour_foreach (contour, 0.001, convex_cb, &c);
g_assert (c.convexity != GSK_CONVEXITY_UNKNOWN);
return c.convexity;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "gskcontourprivate.h"
typedef enum
{
GSK_CONVEXITY_UNKNOWN,
GSK_CONVEXITY_CONVEX,
GSK_CONVEXITY_CONCAVE,
} GskConvexity;
GskConvexity gsk_contour_compute_convexity (const GskContour *contour);
-2003
View File
File diff suppressed because it is too large Load Diff
-621
View File
@@ -1,621 +0,0 @@
/*
* Copyright © 2020 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gskcurveprivate.h"
static inline gboolean
acceptable (float t)
{
return 0 - FLT_EPSILON <= t && t <= 1 + FLT_EPSILON;
}
static int
line_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *pts1 = curve1->line.points;
const graphene_point_t *pts2 = curve2->line.points;
float a1 = pts1[0].x - pts1[1].x;
float b1 = pts1[0].y - pts1[1].y;
float a2 = pts2[0].x - pts2[1].x;
float b2 = pts2[0].y - pts2[1].y;
float det = a1 * b2 - b1 * a2;
if (fabs(det) > 0.01)
{
float tt = ((pts1[0].x - pts2[0].x) * b2 - (pts1[0].y - pts2[0].y) * a2) / det;
float ss = - ((pts1[0].y - pts2[0].y) * a1 - (pts1[0].x - pts2[0].x) * b1) / det;
if (acceptable (tt) && acceptable (ss))
{
p[0].x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
p[0].y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
t1[0] = tt;
t2[0] = ss;
return 1;
}
}
else /* parallel lines */
{
float r = a1 * (pts1[1].y - pts2[0].y) - (pts1[1].x - pts2[0].x) * b1;
float dist = (r * r) / (a1 * a1 + b1 * b1);
float t, s, tt, ss;
if (dist > 0.01)
return 0;
if (pts1[1].x != pts1[0].x)
{
t = (pts2[0].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
s = (pts2[1].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
}
else
{
t = (pts2[0].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
s = (pts2[1].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
}
if ((t < 0 && s < 0) || (t > 1 && s > 1))
return 0;
if (acceptable (t))
{
t1[0] = t;
t2[0] = 0;
p[0] = pts2[0];
}
else if (t < 0)
{
if (pts2[1].x != pts2[0].x)
tt = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
tt = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[0] = 0;
t2[0] = tt;
p[0] = pts1[0];
}
else
{
if (pts2[1].x != pts2[0].x)
tt = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
tt = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[0] = 1;
t2[0] = tt;
p[0] = pts1[1];
}
if (acceptable (s))
{
if (t2[0] == 1)
return 1;
t1[1] = s;
t2[1] = 1;
p[1] = pts2[1];
}
else if (s < 0)
{
if (t1[0] == 0)
return 1;
if (pts2[1].x != pts2[0].x)
ss = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
ss = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[1] = 0;
t2[1] = ss;
p[1] = pts1[0];
}
else
{
if (t1[0] == 1)
return 1;
if (pts2[1].x != pts2[0].x)
ss = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
ss = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[1] = 1;
t2[1] = ss;
p[1] = pts1[1];
}
return 2;
}
return 0;
}
static void
get_tangent (const graphene_point_t *p0,
const graphene_point_t *p1,
graphene_vec2_t *t)
{
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
graphene_vec2_normalize (t, t);
}
static void
align_points (const graphene_point_t *p,
const graphene_point_t *a,
const graphene_point_t *b,
graphene_point_t *q,
int n)
{
graphene_vec2_t n1;
float angle;
float s, c;
float dist;
get_tangent (a, b, &n1);
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
sincosf (angle, &s, &c);
dist = sqrtf ((a->x - b->x)*(a->x - b->x) + (a->y - b->y)*(a->y - b->y));
for (int i = 0; i < n; i++)
{
q[i].x = ((p[i].x - a->x) * c - (p[i].y - a->y) * s) / dist;
q[i].y = ((p[i].x - a->x) * s + (p[i].y - a->y) * c) / dist;
}
}
static void
find_point_on_line (const graphene_point_t *p1,
const graphene_point_t *p2,
const graphene_point_t *q,
float *t)
{
if (p2->x != p1->x)
*t = (q->x - p1->x) / (p2->x - p1->x);
else
*t = (q->y - p1->y) / (p2->y - p1->y);
}
static float
cuberoot (float v)
{
if (v < 0)
return -pow (-v, 1.f / 3);
return pow (v, 1.f / 3);
}
/* Solve P = 0 where P is
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
*/
static int
get_cubic_roots (float pa, float pb, float pc, float pd, float roots[3])
{
float a, b, c, d;
float q, q2;
float p, p3;
float discriminant;
float u1, v1, sd;
int n_roots = 0;
d = -pa + 3*pb - 3*pc + pd;
a = 3*pa - 6*pb + 3*pc;
b = -3*pa + 3*pb;
c = pa;
if (fabs (d) < 0.0001)
{
if (fabs (a) < 0.0001)
{
if (fabs (b) < 0.0001)
return 0;
if (acceptable (-c / b))
{
roots[0] = -c / b;
return 1;
}
return 0;
}
q = sqrt (b*b - 4*a*c);
roots[n_roots] = (-b + q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = (-b - q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
a /= d;
b /= d;
c /= d;
p = (3*b - a*a)/3;
p3 = p/3;
q = (2*a*a*a - 9*a*b + 27*c)/27;
q2 = q/2;
discriminant = q2*q2 + p3*p3*p3;
if (discriminant < 0)
{
float mp3 = -p/3;
float mp33 = mp3*mp3*mp3;
float r = sqrt (mp33);
float t = -q / (2*r);
float cosphi = t < -1 ? -1 : (t > 1 ? 1 : t);
float phi = acos (cosphi);
float crtr = cuberoot (r);
float t1 = 2*crtr;
roots[n_roots] = t1 * cos (phi/3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 2*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 4*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
if (discriminant == 0)
{
u1 = q2 < 0 ? cuberoot (-q2) : -cuberoot (q2);
roots[n_roots] = 2*u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = -u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
sd = sqrt (discriminant);
u1 = cuberoot (sd - q2);
v1 = cuberoot (sd + q2);
roots[n_roots] = u1 - v1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
static int
line_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *a = &curve1->line.points[0];
const graphene_point_t *b = &curve1->line.points[1];
graphene_point_t pts[4];
float t[3];
int m, i, j;
/* Rotate things to place curve1 on the x axis,
* then solve curve2 for y == 0.
*/
align_points (curve2->curve.points, a, b, pts, 4);
m = get_cubic_roots (pts[0].y, pts[1].y, pts[2].y, pts[3].y, t);
j = 0;
for (i = 0; i < m; i++)
{
t2[j] = t[i];
gsk_curve_get_point (curve2, t2[j], &p[j]);
find_point_on_line (a, b, &p[j], &t1[j]);
if (acceptable (t1[j]))
j++;
if (j == n)
break;
}
return j;
}
static void
curve_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos,
float tolerance)
{
GskCurve p11, p12, p21, p22;
GskBoundingBox b1, b2;
float d1, d2;
if (*pos == n)
return;
gsk_curve_get_tight_bounds (curve1, &b1);
gsk_curve_get_tight_bounds (curve2, &b2);
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, 0.5, &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
gsk_curve_split (curve1, 0.5, &p11, &p12);
gsk_curve_split (curve2, 0.5, &p21, &p22);
curve_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
}
static int
curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n,
float tolerance)
{
int pos = 0;
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance);
return pos;
}
static void
get_bounds (const GskCurve *curve,
float tl,
float tr,
GskBoundingBox *bounds)
{
GskCurve c;
gsk_curve_segment (curve, tl, tr, &c);
gsk_curve_get_tight_bounds (&c, bounds);
}
static void
general_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos,
float tolerance)
{
GskBoundingBox b1, b2;
float d1, d2;
if (*pos == n)
return;
get_bounds (curve1, t1l, t1r, &b1);
get_bounds (curve2, t2l, t2r, &b2);
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, t1[*pos], &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], tolerance))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
/* Note that in the conic case, we cannot just split the curves and
* pass the two halves down, since splitting changes the parametrization,
* and we need the t's to be valid parameters wrt to the original curve.
*
* So, instead, we determine the bounding boxes above by always starting
* from the original curve. That is a bit less efficient, but also works
* for conics.
*/
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
}
static int
general_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n,
float tolerance)
{
int pos = 0;
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance);
return pos;
}
static int
curve_self_intersect (const GskCurve *curve,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
float tt[3], ss[3], s;
graphene_point_t pp[3];
int m;
GskCurve cs, ce;
if (curve->op != GSK_PATH_CURVE)
return 0;
s = 0.5;
m = gsk_curve_get_curvature_points (curve, tt);
for (int i = 0; i < m; i++)
{
if (gsk_curve_get_curvature (curve, tt[i], NULL) == 0)
{
s = tt[i];
break;
}
}
gsk_curve_split (curve, s, &cs, &ce);
m = curve_intersect (&cs, &ce, tt, ss, pp, 3, 0.001);
if (m > 1)
{
/* One of the (at most 2) intersections we found
* must be the common point where we split the curve.
* It will have a t value of 1 and an s value of 0.
*/
if (fabs (tt[0] - 1) > 1e-3)
{
t1[0] = t2[0] = tt[0] * s;
p[0] = pp[0];
}
else if (fabs (tt[1] - 1) > 1e-3)
{
t1[0] = t2[0] = tt[1] * s;
p[0] = pp[1];
}
if (fabs (ss[0]) > 1e-3)
{
t1[1] = t2[1] = s + ss[0] * (1 - s);
p[1] = pp[0];
}
else if (fabs (ss[1]) > 1e-3)
{
t1[1] = t2[1] = s + ss[1] * (1 - s);
p[1] = pp[1];
}
return 2;
}
return 0;
}
/* Place intersections between the curves in p, and their Bezier positions
* in t1 and t2, up to n. Return the number of intersections found.
*
* Note that two cubic Beziers can have up to 9 intersections.
*/
int
gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
GskPathOperation op1 = curve1->op;
GskPathOperation op2 = curve2->op;
if (op1 == GSK_PATH_CLOSE)
op1 = GSK_PATH_LINE;
if (op2 == GSK_PATH_CLOSE)
op2 = GSK_PATH_LINE;
if (memcmp (curve1, curve2, sizeof (GskCurve)) == 0)
return curve_self_intersect (curve1, t1, t2, p, n);
/* We special-case line-line and line-curve intersections,
* since we can solve them directly.
* Everything else is done via bisection.
*/
if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_LINE)
return line_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CURVE)
return line_curve_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_LINE)
return line_curve_intersect (curve2, curve1, t2, t1, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_CURVE)
return curve_intersect (curve1, curve2, t1, t2, p, n, 0.001);
else
return general_intersect (curve1, curve2, t1, t2, p, n, 0.001);
}
-164
View File
@@ -1,164 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CURVE_PRIVATE_H__
#define __GSK_CURVE_PRIVATE_H__
#include "gskpathopprivate.h"
#include "gskboundingboxprivate.h"
G_BEGIN_DECLS
typedef gpointer gskpathop;
typedef union _GskCurve GskCurve;
typedef struct _GskLineCurve GskLineCurve;
typedef struct _GskCurveCurve GskCurveCurve;
typedef struct _GskConicCurve GskConicCurve;
struct _GskLineCurve
{
GskPathOperation op;
gboolean padding;
graphene_point_t points[2];
};
struct _GskCurveCurve
{
GskPathOperation op;
gboolean has_coefficients;
graphene_point_t points[4];
graphene_point_t coeffs[4];
};
struct _GskConicCurve
{
GskPathOperation op;
gboolean has_coefficients;
/* points[0], points[1], points[3] are the control points,
* points[2].x is the weight
*/
graphene_point_t points[4];
graphene_point_t num[3];
graphene_point_t denom[3];
};
union _GskCurve
{
GskPathOperation op;
GskLineCurve line;
GskCurveCurve curve;
GskConicCurve conic;
};
typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
const graphene_point_t *to,
float from_progress,
float to_progress,
gpointer user_data);
typedef gboolean (* GskCurveAddCurveFunc) (const graphene_point_t points[4],
gpointer user_data);
void gsk_curve_init (GskCurve *curve,
gskpathop op);
void gsk_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight);
void gsk_curve_get_point (const GskCurve *curve,
float progress,
graphene_point_t *pos);
void gsk_curve_get_tangent (const GskCurve *curve,
float progress,
graphene_vec2_t *tangent);
void gsk_curve_get_normal (const GskCurve *curve,
float progress,
graphene_vec2_t *normal);
void gsk_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end);
void gsk_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment);
gboolean gsk_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data);
gboolean gsk_curve_decompose_curve (const GskCurve *curve,
float tolerance,
GskCurveAddCurveFunc add_curve_func,
gpointer user_data);
gskpathop gsk_curve_pathop (const GskCurve *curve);
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
void gsk_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_bounds (const GskCurve *curve,
GskBoundingBox *bounds);
void gsk_curve_get_tight_bounds (const GskCurve *curve,
GskBoundingBox *bounds);
int gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n);
void gsk_curve_offset (const GskCurve *curve,
float distance,
GskCurve *offset_curve);
void gsk_curve_reverse (const GskCurve *curve,
GskCurve *reverse);
float gsk_curve_get_curvature (const GskCurve *curve,
float t,
graphene_point_t *center);
int gsk_curve_get_curvature_points (const GskCurve *curve,
float t[3]);
int gsk_curve_get_cusps (const GskCurve *curve,
float t[2]);
void gsk_curve_print (const GskCurve *curve);
G_END_DECLS
#endif /* __GSK_CURVE_PRIVATE_H__ */
-1
View File
@@ -10,7 +10,6 @@ static const GdkDebugKey gsk_debug_keys[] = {
{ "surface", GSK_DEBUG_SURFACE, "Information about surfaces" },
{ "fallback", GSK_DEBUG_FALLBACK, "Information about fallbacks" },
{ "glyphcache", GSK_DEBUG_GLYPH_CACHE, "Information about glyph caching" },
{ "paths", GSK_DEBUG_PATHS, "Information about path processing" },
{ "geometry", GSK_DEBUG_GEOMETRY, "Show borders (when using cairo)" },
{ "full-redraw", GSK_DEBUG_FULL_REDRAW, "Force full redraws" },
{ "sync", GSK_DEBUG_SYNC, "Sync after each frame" },
-1
View File
@@ -14,7 +14,6 @@ typedef enum {
GSK_DEBUG_VULKAN = 1 << 5,
GSK_DEBUG_FALLBACK = 1 << 6,
GSK_DEBUG_GLYPH_CACHE = 1 << 7,
GSK_DEBUG_PATHS = 1 << 8,
/* flags below may affect behavior */
GSK_DEBUG_GEOMETRY = 1 << 9,
GSK_DEBUG_FULL_REDRAW = 1 << 10,
+3 -105
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
@@ -50,7 +50,7 @@
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
* The type of a node determines what the node is rendering.
*/
typedef enum {
@@ -73,8 +73,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,
@@ -169,107 +167,6 @@ typedef enum {
GSK_CORNER_BOTTOM_LEFT
} GskCorner;
/**
* GskFillRule:
* @GSK_FILL_RULE_WINDING: If the path crosses the ray from
* left-to-right, counts +1. If the path crosses the ray
* from right to left, counts -1. (Left and right are determined
* from the perspective of looking along the ray from the starting
* point.) If the total count is non-zero, the point will be filled.
* @GSK_FILL_RULE_EVEN_ODD: Counts the total number of
* intersections, without regard to the orientation of the contour. If
* the total number of intersections is odd, the point will be
* filled.
*
* #GskFillRule is used to select how paths are filled, for example in
* gsk_fill_node_new(). Whether or not a point is included in the fill is
* determined by taking a ray from that point to infinity and looking
* at intersections with the path. The ray can be in any direction,
* as long as it doesn't pass through the end point of a segment
* or have a tricky intersection such as intersecting tangent to the path.
* (Note that filling is not actually implemented in this way. This
* is just a description of the rule that is applied.)
*
* New entries may be added in future versions.
**/
typedef enum {
GSK_FILL_RULE_WINDING,
GSK_FILL_RULE_EVEN_ODD
} GskFillRule;
/**
* GskLineCap:
* @GSK_LINE_CAP_BUTT: Start and stop the line exactly at the start
* and end point
* @GSK_LINE_CAP_ROUND: Use a round ending, the center of the circle
* is the start or end point.
* @GSK_LINE_CAP_SQUARE: use squared ending, the center of the square
* is the start or end point.
*
* Specifies how to render the start and end points of contours or
* dashes when stroking.
*
* The default line cap style is @GSK_LINE_CAP_BUTT.
*/
typedef enum {
GSK_LINE_CAP_BUTT,
GSK_LINE_CAP_ROUND,
GSK_LINE_CAP_SQUARE
} GskLineCap;
/**
* GskLineJoin:
* @GSK_LINE_JOIN_MITER: Use a sharp, angled corner
* @GSK_LINE_JOIN_MITER_CLIP: Use a sharp, angled corner, at a distance
* @GSK_LINE_JOIN_ROUND: Use a round join, the center of the circle is
* the joint point
* @GSK_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
* the line width from the joint point
* @GSK_LINE_JOIN_ARCS: Use a sharp angled corner made from circles
*
* Specifies how to render the junction of two lines when stroking.
*
* See [method@Gsk.Stroke.set_miter_limit] for details on the difference
* between @GSK_LINE_JOIN_MITER and @GSK_LINE_JOIN_MITER_CLIP.
*
* The default line join style is @GSK_LINE_JOIN_MITER.
**/
typedef enum {
GSK_LINE_JOIN_MITER,
GSK_LINE_JOIN_MITER_CLIP,
GSK_LINE_JOIN_ROUND,
GSK_LINE_JOIN_BEVEL,
GSK_LINE_JOIN_ARCS
} GskLineJoin;
/**
* GskPathOperation:
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the
* target point.
* @GSK_PATH_LINE: A line-to operation, with 2 points describing the
* start and end point of a straight line.
* @GSK_PATH_CLOSE: A close operation ending the current contour with
* a line back to the starting point. Two points describe the start
* and end of the line.
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
* with 4 points describing the start point, the two control points
* and the end point of the curve.
* @GSK_PATH_CONIC: A weighted quadratic Bézier curve with 3 points
* describing the start point, control point and end point of the
* curve. A weight for the curve will be passed, too.
*
* Path operations can be used to approximate a #GskPath.
*
* More values may be added in the future.
**/
typedef enum {
GSK_PATH_MOVE,
GSK_PATH_CLOSE,
GSK_PATH_LINE,
GSK_PATH_CURVE,
GSK_PATH_CONIC,
} GskPathOperation;
/**
* GskSerializationError:
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be identified
@@ -354,4 +251,5 @@ typedef enum
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */
-1516
View File
File diff suppressed because it is too large Load Diff
-154
View File
@@ -1,154 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_H__
#define __GSK_PATH_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
/**
* GskPathForeachFlags:
* @GSK_PATH_FOREACH_ALLOW_CURVE: Allow emission of `GSK_PATH_CURVE` operations.
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of `GSK_PATH_CONIC` operations.
*
* Flags that can be passed to [method@Gsk.Path.foreach] to enable additional
* features.
*
* 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`.
*/
typedef enum
{
GSK_PATH_FOREACH_ALLOW_CURVE = (1 << 0),
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
} GskPathForeachFlags;
/**
* GskPathForeachFunc:
* @op: The operation to perform
* @pts: The points of the operation
* @n_pts: The number of points
* @weight: The weight for conic curves, or unused if not a conic curve.
* @user_data: The user data provided with the function
*
* Prototype of the callback to iterate throught the operations of
* a path.
*
* Returns: %TRUE to continue evaluating the path, %FALSE to
* immediately abort and not call the function again.
*/
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data);
#define GSK_TYPE_PATH (gsk_path_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_new_from_cairo (const cairo_path_t *path);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_ref (GskPath *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_unref (GskPath *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_print (GskPath *self,
GString *string);
GDK_AVAILABLE_IN_ALL
char * gsk_path_to_string (GskPath *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_parse (const char *string);
GDK_AVAILABLE_IN_ALL
void gsk_path_to_cairo (GskPath *self,
cairo_t *cr);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_is_empty (GskPath *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_get_bounds (GskPath *self,
graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_get_stroke_bounds (GskPath *path,
const GskStroke *stroke,
graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_is_convex (GskPath *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_foreach (GskPath *self,
GskPathForeachFlags flags,
GskPathForeachFunc func,
gpointer user_data);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_reverse (GskPath *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_transform (GskPath *self,
GskTransform *transform);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_stroke (GskPath *self,
GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_offset (GskPath *self,
float distance,
GskLineJoin line_join,
float miter_limit);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_union (GskPath *first,
GskPath *second);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_intersection (GskPath *first,
GskPath *second);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_difference (GskPath *first,
GskPath *second);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_symmetric_difference (GskPath *first,
GskPath *second);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_simplify (GskPath *self);
G_END_DECLS
#endif /* __GSK_PATH_H__ */
-964
View File
@@ -1,964 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathbuilder.h"
#include "gskpathprivate.h"
/**
* GskPathBuilder:
*
* A `GskPathBuilder` is an auxiliary object that is used to
* create new `GskPath` objects.
*
* A path is constructed like this:
*
* ```
* GskPath *
* construct_path (void)
* {
* GskPathBuilder *builder;
*
* builder = gsk_path_builder_new ();
*
* // add contours to the path here
*
* return gsk_path_builder_free_to_path (builder);
* }
* ```
*
* Adding contours to the path can be done in two ways.
* The easiest option is to use the `gsk_path_builder_add_*` group
* of functions that add predefined contours to the current path,
* either common shapes like [method@Gsk.PathBuilder.add_circle]
* or by adding from other paths like [method@Gsk.PathBuilder.add_path].
*
* 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
* and then use multiple calls to any of the drawing functions to
* move the pen along the plane. Once you are done, you can call
* [method@Gsk.PathBuilder.close] to close the path by connecting it
* back with a line to the starting point.
* This is similar for how paths are drawn in Cairo.
*/
struct _GskPathBuilder
{
int ref_count;
GSList *contours; /* (reverse) list of already recorded contours */
GskPathFlags flags; /* flags for the current path */
graphene_point_t current_point; /* the point all drawing ops start from */
GArray *ops; /* operations for current contour - size == 0 means no current contour */
GArray *points; /* points for the operations */
};
G_DEFINE_BOXED_TYPE (GskPathBuilder,
gsk_path_builder,
gsk_path_builder_ref,
gsk_path_builder_unref)
/**
* gsk_path_builder_new:
*
* Create a new `GskPathBuilder` object. The resulting builder
* would create an empty `GskPath`. Use addition functions to add
* types to it.
*
* Returns: a new `GskPathBuilder`
**/
GskPathBuilder *
gsk_path_builder_new (void)
{
GskPathBuilder *builder;
builder = g_slice_new0 (GskPathBuilder);
builder->ref_count = 1;
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
/* Be explicit here */
builder->current_point = GRAPHENE_POINT_INIT (0, 0);
return builder;
}
/**
* gsk_path_builder_ref:
* @builder: a `GskPathBuilder`
*
* Acquires a reference on the given @builder.
*
* This function is intended primarily for bindings. `GskPathBuilder` objects
* should not be kept around.
*
* Returns: (transfer none): the given `GskPathBuilder` with
* its reference count increased
*/
GskPathBuilder *
gsk_path_builder_ref (GskPathBuilder *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
g_return_val_if_fail (builder->ref_count > 0, NULL);
builder->ref_count += 1;
return builder;
}
/* We're cheating here. Out pathops are relative to the NULL pointer,
* so that we can not care about the points GArray reallocating itself
* until we create the contour.
* This does however mean that we need to not use gsk_pathop_get_points()
* without offsetting the returned pointer.
*/
static inline gskpathop
gsk_pathop_encode_index (GskPathOperation op,
gsize index)
{
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
}
static void
gsk_path_builder_ensure_current (GskPathBuilder *builder)
{
if (builder->ops->len != 0)
return;
builder->flags = GSK_PATH_FLAT;
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (GSK_PATH_MOVE, 0) }, 1);
g_array_append_val (builder->points, builder->current_point);
}
static void
gsk_path_builder_append_current (GskPathBuilder *builder,
GskPathOperation op,
gsize n_points,
const graphene_point_t *points)
{
gsk_path_builder_ensure_current (builder);
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (op, builder->points->len - 1) }, 1);
g_array_append_vals (builder->points, points, n_points);
builder->current_point = points[n_points - 1];
}
static void
gsk_path_builder_end_current (GskPathBuilder *builder)
{
GskContour *contour;
if (builder->ops->len == 0)
return;
contour = gsk_standard_contour_new (builder->flags,
(graphene_point_t *) builder->points->data,
builder->points->len,
(gskpathop *) builder->ops->data,
builder->ops->len,
(graphene_point_t *) builder->points->data - (graphene_point_t *) NULL);
g_array_set_size (builder->ops, 0);
g_array_set_size (builder->points, 0);
/* do this at the end to avoid inflooping when add_contour calls back here */
gsk_path_builder_add_contour (builder, contour);
}
static void
gsk_path_builder_clear (GskPathBuilder *builder)
{
gsk_path_builder_end_current (builder);
g_slist_free_full (builder->contours, g_free);
builder->contours = NULL;
}
/**
* gsk_path_builder_unref:
* @builder: a `GskPathBuilder`
*
* Releases a reference on the given @builder.
*/
void
gsk_path_builder_unref (GskPathBuilder *builder)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (builder->ref_count > 0);
builder->ref_count -= 1;
if (builder->ref_count > 0)
return;
gsk_path_builder_clear (builder);
g_array_unref (builder->ops);
g_array_unref (builder->points);
g_slice_free (GskPathBuilder, builder);
}
/**
* gsk_path_builder_free_to_path: (skip)
* @builder: a `GskPathBuilder`
*
* Creates a new `GskPath` from the current state of the
* given @builder, and frees the @builder instance.
*
* Returns: (transfer full): the newly created `GskPath`
* with all the contours added to @builder
*/
GskPath *
gsk_path_builder_free_to_path (GskPathBuilder *builder)
{
GskPath *res;
g_return_val_if_fail (builder != NULL, NULL);
res = gsk_path_builder_to_path (builder);
gsk_path_builder_unref (builder);
return res;
}
/**
* gsk_path_builder_to_path:
* @builder: a `GskPathBuilder`
*
* Creates a new `GskPath` from the given @builder.
*
* The given `GskPathBuilder` is reset once this function returns;
* you cannot call this function multiple times on the same @builder instance.
*
* This function is intended primarily for bindings. C code should use
* gsk_path_builder_free_to_path().
*
* Returns: (transfer full): the newly created `GskPath`
* with all the contours added to @builder
*/
GskPath *
gsk_path_builder_to_path (GskPathBuilder *builder)
{
GskPath *path;
g_return_val_if_fail (builder != NULL, NULL);
gsk_path_builder_end_current (builder);
builder->contours = g_slist_reverse (builder->contours);
path = gsk_path_new_from_contours (builder->contours);
gsk_path_builder_clear (builder);
return path;
}
void
gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour)
{
gsk_path_builder_end_current (builder);
builder->contours = g_slist_prepend (builder->contours, contour);
}
/**
* gsk_path_builder_get_current_point:
* @builder: a #GskPathBuilder
*
* Gets the current point. The current point is used for relative
* drawing commands and updated after every operation.
*
* When @builder is created, the default current point is set to (0, 0).
*
* Returns: (transfer none): The current point
**/
const graphene_point_t *
gsk_path_builder_get_current_point (GskPathBuilder *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
return &builder->current_point;
}
/**
* gsk_path_builder_add_path:
* @builder: a `GskPathBuilder`
* @path: (transfer none): the path to append
*
* Appends all of @path to @builder.
**/
void
gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path)
{
gsize i;
g_return_if_fail (builder != NULL);
g_return_if_fail (path != NULL);
for (i = 0; i < gsk_path_get_n_contours (path); i++)
{
const GskContour *contour = gsk_path_get_contour (path, i);
gsk_path_builder_add_contour (builder, gsk_contour_dup (contour));
}
}
/**
* gsk_path_builder_add_rect:
* @builder: A `GskPathBuilder`
* @rect: The rectangle to add to @builder
*
* Adds a path representing the given rectangle.
*
* If the width or height of the rectangle is negative, the start
* point will be on the right or bottom, respectively.
*
* If the the width or height are 0, the path will be a closed
* horizontal or vertical line. If both are 0, it'll be a closed dot.
**/
void
gsk_path_builder_add_rect (GskPathBuilder *builder,
const graphene_rect_t *rect)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
contour = gsk_rect_contour_new (rect);
gsk_path_builder_add_contour (builder, contour);
gsk_contour_get_start_end (contour, NULL, &builder->current_point);
}
/**
* gsk_path_builder_add_rounded_rect:
* @builder: a `GskPathBuilder`
* @rect: the rounded rect
*
* Adds @rect as a new contour to the path built in @builder.
**/
void
gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
const GskRoundedRect *rect)
{
const float weight = sqrt(0.5f);
g_return_if_fail (builder != NULL);
g_return_if_fail (rect != NULL);
gsk_path_builder_move_to (builder,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* top */
gsk_path_builder_line_to (builder,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->bounds.origin.y);
/* topright corner */
gsk_path_builder_conic_to (builder,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height,
weight);
/* right */
gsk_path_builder_line_to (builder,
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_conic_to (builder,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
rect->bounds.origin.y + rect->bounds.size.height,
weight);
/* bottom */
gsk_path_builder_line_to (builder,
rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottomleft corner */
gsk_path_builder_conic_to (builder,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
weight);
/* left */
gsk_path_builder_line_to (builder,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
/* topleft corner */
gsk_path_builder_conic_to (builder,
rect->bounds.origin.x,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y,
weight);
/* done */
gsk_path_builder_close (builder);
}
/**
* gsk_path_builder_add_circle:
* @builder: a `GskPathBuilder`
* @center: the center of the circle
* @radius: the radius of the circle
*
* Adds a circle with the @center and @radius.
**/
void
gsk_path_builder_add_circle (GskPathBuilder *builder,
const graphene_point_t *center,
float radius)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (radius > 0);
contour = gsk_circle_contour_new (center, radius, 0, 360);
gsk_path_builder_add_contour (builder, contour);
}
/**
* gsk_path_builder_add_ellipse:
* @builder: a `GskPathBuilder`
* @center: the center point of the ellipse
* @radius: the radius of the ellipse in x/y direction
*
* Adds an ellipse with the given @center and the @radius in
* x/y direction.
**/
void
gsk_path_builder_add_ellipse (GskPathBuilder *builder,
const graphene_point_t *center,
const graphene_size_t *radius)
{
const float weight = sqrt(0.5f);
graphene_point_t pts[8];
g_return_if_fail (builder != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (radius != NULL);
pts[0] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y);
pts[1] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y + radius->height / 2);
pts[2] = GRAPHENE_POINT_INIT (center->x,
center->y + radius->height / 2);
pts[3] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y + radius->height / 2);
pts[4] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y);
pts[5] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y - radius->height / 2);
pts[6] = GRAPHENE_POINT_INIT (center->x,
center->y - radius->height / 2);
pts[7] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y - radius->height / 2);
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
gsk_path_builder_conic_to (builder, pts[3].x, pts[3].y, pts[4].x, pts[4].y, weight);
gsk_path_builder_conic_to (builder, pts[5].x, pts[5].y, pts[6].x, pts[6].y, weight);
gsk_path_builder_conic_to (builder, pts[7].x, pts[7].y, pts[0].x, pts[0].y, weight);
gsk_path_builder_close (builder);
}
/**
* gsk_path_builder_move_to:
* @builder: a `GskPathBuilder`
* @x: x coordinate
* @y: y coordinate
*
* Starts a new contour by placing the pen at @x, @y.
*
* If `gsk_path_builder_move_to()` is called twice in succession,
* the first call will result in a contour made up of a single point.
* The second call will start a new contour.
**/
void
gsk_path_builder_move_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_end_current (builder);
builder->current_point = GRAPHENE_POINT_INIT(x, y);
gsk_path_builder_ensure_current (builder);
}
/**
* gsk_path_builder_rel_move_to:
* @builder: a `GskPathBuilder`
* @x: x offset
* @y: y offset
*
* Starts a new contour by placing the pen at @x, @y relative to the current
* point.
*
* This is the relative version of [method@Gsk.PathBuilder.move_to].
**/
void
gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_move_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_line_to:
* @builder: a `GskPathBuilder`
* @x: x coordinate
* @y: y coordinate
*
* Draws a line from the current point to @x, @y and makes it the new current
* point.
**/
void
gsk_path_builder_line_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
/* skip the line if it goes to the same point */
if (graphene_point_equal (&builder->current_point,
&GRAPHENE_POINT_INIT (x, y)))
return;
gsk_path_builder_append_current (builder,
GSK_PATH_LINE,
1, (graphene_point_t[1]) {
GRAPHENE_POINT_INIT (x, y)
});
}
/**
* gsk_path_builder_rel_line_to:
* @builder: a `GskPathBuilder`
* @x: x offset
* @y: y offset
*
* Draws a line from the current point to a point offset to it by @x, @y
* and makes it the new current point.
*
* This is the relative version of [method@Gsk.PathBuilder.line_to].
**/
void
gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_line_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_curve_to:
* @builder: a `GskPathBuilder`
* @x1: x coordinate of first control point
* @y1: y coordinate of first control point
* @x2: x coordinate of second control point
* @y2: y coordinate of second control point
* @x3: x coordinate of the end of the curve
* @y3: y coordinate of the end of the curve
*
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
* points.
*
* After this, @x3, @y3 will be the new current point.
**/
void
gsk_path_builder_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CURVE,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (x2, y2),
GRAPHENE_POINT_INIT (x3, y3)
});
}
/**
* gsk_path_builder_rel_curve_to:
* @builder: a `GskPathBuilder`
* @x1: x offset of first control point
* @y1: y offset of first control point
* @x2: x offset of second control point
* @y2: y offset of second control point
* @x3: x offset of the end of the curve
* @y3: y offset of the end of the curve
*
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
* points. All coordinates are given relative to the current point.
*
* This is the relative version of [method@Gsk.PathBuilder.curve_to].
**/
void
gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_curve_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
builder->current_point.x + x3,
builder->current_point.y + y3);
}
/**
* gsk_path_builder_conic_to:
* @builder: a `GskPathBuilder`
* @x1: x coordinate of control point
* @y1: y coordinate of control point
* @x2: x coordinate of the end of the curve
* @y2: y coordinate of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* Conic curves can be used to draw ellipses and circles.
*
* After this, @x2, @y2 will be the new current point.
**/
void
gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CONIC,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (weight, 0),
GRAPHENE_POINT_INIT (x2, y2)
});
}
/**
* gsk_path_builder_rel_conic_to:
* @builder: a `GskPathBuilder`
* @x1: x offset of control point
* @y1: y offset of control point
* @x2: x offset of the end of the curve
* @y2: y offset of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* This is the relative version of [method@Gsk.PathBuilder.conic_to].
**/
void
gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
gsk_path_builder_conic_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
weight);
}
/**
* gsk_path_builder_close:
* @builder: a `GskPathBuilder`
*
* Ends the current contour with a line back to the start point.
*
* Note that this is different from calling [method@Gsk.PathBuilder.line_to]
* with the start point in that the contour will be closed. A closed
* contour behaves different from an open one when stroking its start
* and end point are considered connected, so they will be joined
* via the line join, and not ended with line caps.
**/
void
gsk_path_builder_close (GskPathBuilder *builder)
{
g_return_if_fail (builder != NULL);
if (builder->ops->len == 0)
return;
builder->flags |= GSK_PATH_CLOSED;
gsk_path_builder_append_current (builder,
GSK_PATH_CLOSE,
1, (graphene_point_t[1]) {
g_array_index (builder->points, graphene_point_t, 0)
});
gsk_path_builder_end_current (builder);
}
static void
arc_segment (GskPathBuilder *builder,
double cx,
double cy,
double rx,
double ry,
double sin_phi,
double cos_phi,
double sin_th0,
double cos_th0,
double sin_th1,
double cos_th1,
double t)
{
double x1, y1, x2, y2, x3, y3;
x1 = rx * (cos_th0 - t * sin_th0);
y1 = ry * (sin_th0 + t * cos_th0);
x3 = rx * cos_th1;
y3 = ry * sin_th1;
x2 = x3 + rx * (t * sin_th1);
y2 = y3 + ry * (-t * cos_th1);
gsk_path_builder_curve_to (builder,
cx + cos_phi * x1 - sin_phi * y1,
cy + sin_phi * x1 + cos_phi * y1,
cx + cos_phi * x2 - sin_phi * y2,
cy + sin_phi * x2 + cos_phi * y2,
cx + cos_phi * x3 - sin_phi * y3,
cy + sin_phi * x3 + cos_phi * y3);
}
void
gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
float rx,
float ry,
float x_axis_rotation,
gboolean large_arc,
gboolean positive_sweep,
float x,
float y)
{
graphene_point_t *current;
double x1, y1, x2, y2;
double phi, sin_phi, cos_phi;
double mid_x, mid_y;
double lambda;
double d;
double k;
double x1_, y1_;
double cx_, cy_;
double cx, cy;
double ux, uy, u_len;
double cos_theta1, theta1;
double vx, vy, v_len;
double dp_uv;
double cos_delta_theta, delta_theta;
int i, n_segs;
double d_theta, theta;
double sin_th0, cos_th0;
double sin_th1, cos_th1;
double th_half;
double t;
if (builder->points->len > 0)
{
current = &g_array_index (builder->points, graphene_point_t, builder->points->len - 1);
x1 = current->x;
y1 = current->y;
}
else
{
x1 = 0;
y1 = 0;
}
x2 = x;
y2 = y;
phi = x_axis_rotation * M_PI / 180.0;
sincos (phi, &sin_phi, &cos_phi);
rx = fabs (rx);
ry = fabs (ry);
mid_x = (x1 - x2) / 2;
mid_y = (y1 - y2) / 2;
x1_ = cos_phi * mid_x + sin_phi * mid_y;
y1_ = - sin_phi * mid_x + cos_phi * mid_y;
lambda = (x1_ / rx) * (x1_ / rx) + (y1_ / ry) * (y1_ / ry);
if (lambda > 1)
{
lambda = sqrt (lambda);
rx *= lambda;
ry *= lambda;
}
d = (rx * y1_) * (rx * y1_) + (ry * x1_) * (ry * x1_);
if (d == 0)
return;
k = sqrt (fabs ((rx * ry) * (rx * ry) / d - 1.0));
if (positive_sweep == large_arc)
k = -k;
cx_ = k * rx * y1_ / ry;
cy_ = -k * ry * x1_ / rx;
cx = cos_phi * cx_ - sin_phi * cy_ + (x1 + x2) / 2;
cy = sin_phi * cx_ + cos_phi * cy_ + (y1 + y2) / 2;
ux = (x1_ - cx_) / rx;
uy = (y1_ - cy_) / ry;
u_len = sqrt (ux * ux + uy * uy);
if (u_len == 0)
return;
cos_theta1 = CLAMP (ux / u_len, -1, 1);
theta1 = acos (cos_theta1);
if (uy < 0)
theta1 = - theta1;
vx = (- x1_ - cx_) / rx;
vy = (- y1_ - cy_) / ry;
v_len = sqrt (vx * vx + vy * vy);
if (v_len == 0)
return;
dp_uv = ux * vx + uy * vy;
cos_delta_theta = CLAMP (dp_uv / (u_len * v_len), -1, 1);
delta_theta = acos (cos_delta_theta);
if (ux * vy - uy * vx < 0)
delta_theta = - delta_theta;
if (positive_sweep && delta_theta < 0)
delta_theta += 2 * M_PI;
else if (!positive_sweep && delta_theta > 0)
delta_theta -= 2 * M_PI;
n_segs = ceil (fabs (delta_theta / (M_PI_2 + 0.001)));
d_theta = delta_theta / n_segs;
theta = theta1;
sincos (theta1, &sin_th1, &cos_th1);
th_half = d_theta / 2;
t = (8.0 / 3.0) * sin (th_half / 2) * sin (th_half / 2) / sin (th_half);
for (i = 0; i < n_segs; i++)
{
theta = theta1;
theta1 = theta + d_theta;
sin_th0 = sin_th1;
cos_th0 = cos_th1;
sincos (theta1, &sin_th1, &cos_th1);
arc_segment (builder,
cx, cy, rx, ry,
sin_phi, cos_phi,
sin_th0, cos_th0,
sin_th1, cos_th1,
t);
}
}
/**
* gsk_path_builder_add_layout:
* @builder: a `GskPathBuilder`
* @layout: the pango layout to add
*
* Adds the outlines for the glyphs in @layout to @builder.
*/
void
gsk_path_builder_add_layout (GskPathBuilder *builder,
PangoLayout *layout)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *cairo_path;
GskPath *path;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
pango_cairo_layout_path (cr, layout);
cairo_path = cairo_copy_path_flat (cr);
path = gsk_path_new_from_cairo (cairo_path);
gsk_path_builder_add_path (builder, path);
gsk_path_unref (path);
cairo_path_destroy (cairo_path);
cairo_destroy (cr);
cairo_surface_destroy (surface);
}
-132
View File
@@ -1,132 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_BUILDER_H__
#define __GSK_PATH_BUILDER_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gskroundedrect.h>
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathBuilder * gsk_path_builder_new (void);
GDK_AVAILABLE_IN_ALL
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_unref (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rect (GskPathBuilder *builder,
const graphene_rect_t *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
const GskRoundedRect *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_circle (GskPathBuilder *builder,
const graphene_point_t *center,
float radius);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_ellipse (GskPathBuilder *builder,
const graphene_point_t *center,
const graphene_size_t *radius);
/* next function implemented in gskpathmeasure.c */
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_segment (GskPathBuilder *builder,
GskPathMeasure *measure,
float start,
float end);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_close (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_layout (GskPathBuilder *builder,
PangoLayout *layout);
G_END_DECLS
#endif /* __GSK_PATH_BUILDER_H__ */
-291
View File
@@ -1,291 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathdashprivate.h"
#include "gskcontourprivate.h"
#include "gskcurveprivate.h"
#include "gskpathprivate.h"
#include "gskstrokeprivate.h"
typedef struct
{
float offset; /* how much of the current dash we've spent */
gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
for uneven dashes */
gboolean on; /* If we're currently dashing or not */
gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
enum {
NORMAL, /* no special behavior required */
SKIP, /* skip the next dash */
ONLY, /* only do the first dash */
DONE /* done with the first dash */
} first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
twice to make sure the first dash and the last dash can get joined */
GskCurve curve; /* Curve we are currently processing */
float collect_start; /* We're collecting multiple line segments when decomposing. */
float collect_length; /* No need to emit a curve for every line segment when the dash is long enough. */
/* from the stroke */
float *dash;
gsize n_dash;
float dash_length;
float dash_offset;
float tolerance;
GskPathForeachFunc func;
gpointer user_data;
} GskPathDash;
static void
gsk_path_dash_setup (GskPathDash *self)
{
self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
self->dash_index = 0;
self->on = TRUE;
self->may_close = TRUE;
while (self->offset > self->dash[self->dash_index % self->n_dash])
{
self->offset -= self->dash[self->dash_index % self->n_dash];
self->dash_index++;
self->on = !self->on;
}
if (self->first_dash_behavior != ONLY)
self->needs_move_to = TRUE;
}
static gboolean
gsk_path_dash_ensure_move_to (GskPathDash *self,
const graphene_point_t *pt)
{
if (!self->needs_move_to)
return TRUE;
if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
return FALSE;
self->needs_move_to = FALSE;
return TRUE;
}
static gboolean
gsk_path_dash_add_line_segment (const graphene_point_t *start,
const graphene_point_t *end,
float t_start,
float t_end,
gpointer user_data)
{
GskPathDash *self = user_data;
float remaining, length, t_step;
length = graphene_point_distance (start, end, NULL, NULL);
if (self->collect_length)
{
t_start = self->collect_start;
length += self->collect_length;
self->collect_length = 0;
}
t_step = t_end - t_start;
remaining = length;
while (remaining)
{
float piece;
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
/* try collecting multiple line segments */
if (t_end < 1.0)
{
self->collect_start = t_start + t_step * (length - remaining) / length;
self->collect_length = remaining;
return TRUE;
}
piece = remaining;
}
else
piece = self->dash[self->dash_index % self->n_dash] - self->offset;
if (self->on)
{
if (self->first_dash_behavior != SKIP)
{
GskCurve segment;
if (piece)
{
gsk_curve_segment (&self->curve,
t_start + t_step * (length - remaining) / length,
t_start + t_step * (length - (remaining - piece)) / length,
&segment);
if (!gsk_path_dash_ensure_move_to (self, gsk_curve_get_start_point (&segment)))
return FALSE;
if (!gsk_pathop_foreach (gsk_curve_pathop (&segment), self->func, self->user_data))
return FALSE;
}
else
{
graphene_point_t p;
gsk_curve_get_point (&self->curve, t_start + t_step * (length - remaining) / length, &p);
if (!gsk_path_dash_ensure_move_to (self, &p))
return FALSE;
}
}
}
else
{
self->may_close = FALSE;
if (self->first_dash_behavior == ONLY)
{
self->first_dash_behavior = DONE;
return FALSE;
}
self->first_dash_behavior = NORMAL;
}
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
self->offset += remaining;
remaining = 0;
}
else
{
remaining -= piece;
self->offset = 0;
self->dash_index++;
self->dash_index %= 2 * self->n_dash;
self->on = !self->on;
self->needs_move_to = TRUE;
}
}
return TRUE;
}
static gboolean
gsk_path_dash_foreach (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathDash *self = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_dash_setup (self);
break;
case GSK_PATH_CLOSE:
if (self->may_close)
{
if (graphene_point_equal (&pts[0], &pts[1]))
return self->func (GSK_PATH_CLOSE, pts, 2, 0, self->user_data);
}
else
op = GSK_PATH_LINE;
G_GNUC_FALLTHROUGH;
case GSK_PATH_LINE:
case GSK_PATH_CURVE:
case GSK_PATH_CONIC:
gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
return FALSE;
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
gboolean
gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
GskPathDash self = {
.offset = 0,
.dash = stroke->dash,
.n_dash = stroke->n_dash,
.dash_length = stroke->dash_length,
.dash_offset = stroke->dash_offset,
.tolerance = tolerance,
.func = func,
.user_data = user_data
};
gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
self.first_dash_behavior = is_closed ? SKIP : NORMAL;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
return FALSE;
if (is_closed)
{
if (self.first_dash_behavior == NORMAL)
self.first_dash_behavior = ONLY;
else
self.first_dash_behavior = NORMAL;
self.needs_move_to = !self.on;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
self.first_dash_behavior != DONE)
return FALSE;
}
return TRUE;
}
gboolean
gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
gsize i;
/* Dashing disabled, no need to do any work */
if (stroke->dash_length <= 0)
return gsk_path_foreach (path, -1, func, user_data);
for (i = 0; i < gsk_path_get_n_contours (path); i++)
{
if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
return FALSE;
}
return TRUE;
}
-50
View File
@@ -1,50 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_DASH_PRIVATE_H__
#define __GSK_PATH_DASH_PRIVATE_H__
#include <gsk/gskpath.h>
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#ifdef GTK_COMPILATION
#include "gskcontourprivate.h"
gboolean gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#endif /* GTK_COMPILATION */
G_END_DECLS
#endif /* __GSK_PATH_DASH_PRIVATE_H__ */
-677
View File
@@ -1,677 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathmeasure.h"
#include "gskpathbuilder.h"
#include "gskpathbuilder.h"
#include "gskpathprivate.h"
/**
* GskPathMeasure:
*
* `GskPathMeasure` is an object that allows measuring operations
* on `GskPath` objects, to determine quantities like the arc
* length of the path, its curvature, or closest points.
*
* These operations are useful when implementing animations.
*/
typedef struct _GskContourMeasure GskContourMeasure;
struct _GskContourMeasure
{
float length;
gpointer contour_data;
};
struct _GskPathMeasure
{
/*< private >*/
guint ref_count;
GskPath *path;
float tolerance;
gsize first;
gsize last;
float length;
gsize n_contours;
GskContourMeasure measures[];
};
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
gsk_path_measure_ref,
gsk_path_measure_unref)
/**
* gsk_path_measure_new:
* @path: the path to measure
*
* Creates a measure object for the given @path with a
* default tolerance.
*
* Returns: a new `GskPathMeasure` representing @path
**/
GskPathMeasure *
gsk_path_measure_new (GskPath *path)
{
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
}
/**
* gsk_path_measure_new_with_tolerance:
* @path: the path to measure
* @tolerance: the tolerance for measuring operations
*
* Creates a measure object for the given @path and @tolerance.
*
* Returns: a new `GskPathMeasure` representing @path
**/
GskPathMeasure *
gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance)
{
GskPathMeasure *self;
gsize i, n_contours;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (tolerance > 0, NULL);
n_contours = gsk_path_get_n_contours (path);
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
self->ref_count = 1;
self->path = gsk_path_ref (path);
self->tolerance = tolerance;
self->n_contours = n_contours;
self->first = 0;
self->last = n_contours;
for (i = 0; i < n_contours; i++)
{
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i),
self->tolerance,
&self->measures[i].length);
self->length += self->measures[i].length;
}
return self;
}
/**
* gsk_path_measure_ref:
* @self: a `GskPathMeasure`
*
* Increases the reference count of a `GskPathMeasure` by one.
*
* Returns: the passed in `GskPathMeasure`.
**/
GskPathMeasure *
gsk_path_measure_ref (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, NULL);
self->ref_count++;
return self;
}
/**
* gsk_path_measure_unref:
* @self: a `GskPathMeasure`
*
* Decreases the reference count of a `GskPathMeasure` by one.
*
* If the resulting reference count is zero, frees the object.
**/
void
gsk_path_measure_unref (GskPathMeasure *self)
{
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
self->ref_count--;
if (self->ref_count > 0)
return;
for (i = 0; i < self->n_contours; i++)
{
gsk_contour_free_measure (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data);
}
gsk_path_unref (self->path);
g_free (self);
}
/**
* gsk_path_measure_get_path:
* @self: a `GskPathMeasure`
*
* Returns the path that the measure was created for.
*
* Returns: (transfer none): the path of @self
*/
GskPath *
gsk_path_measure_get_path (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->path;
}
/**
* gsk_path_measure_get_tolerance:
* @self: a `GskPathMeasure`
*
* Returns the tolerance that the measure was created with.
*
* Returns: the tolerance of @self
*/
float
gsk_path_measure_get_tolerance (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0.f);
return self->tolerance;
}
/**
* gsk_path_measure_get_n_contours:
* @self: a `GskPathMeasure`
*
* Returns the number of contours in the path being measured.
*
* The returned value is independent of whether @self if restricted
* or not.
*
* Returns: The number of contours
**/
gsize
gsk_path_measure_get_n_contours (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->n_contours;
}
/**
* gsk_path_measure_restrict_to_contour:
* @self: a `GskPathMeasure`
* @contour: contour to restrict to or (gsize) -1 for using the
* whole path
*
* Restricts all functions on the path to just the given @contour.
*
* If @contour >= gsk_path_measure_get_n_contours() - so in
* particular when it is set to -1 - the whole path will be used.
**/
void
gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour)
{
if (contour >= self->n_contours)
{
/* use the whole path */
self->first = 0;
self->last = self->n_contours;
}
else
{
/* use just one contour */
self->first = contour;
self->last = contour + 1;
}
self->length = 0;
for (gsize i = self->first; i < self->last; i++)
{
self->length += self->measures[i].length;
}
}
/**
* gsk_path_measure_get_length:
* @self: a `GskPathMeasure`
*
* Gets the length of the path being measured.
*
* The length is cached, so this function does not do any work.
*
* Returns: The length of the path measured by @self
**/
float
gsk_path_measure_get_length (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->length;
}
/**
* gsk_path_measure_is_closed:
* @self: a `GskPathMeasure`
*
* Returns if the path being measured represents a single closed
* contour.
*
* Returns: %TRUE if the current path is closed
**/
gboolean
gsk_path_measure_is_closed (GskPathMeasure *self)
{
const GskContour *contour;
g_return_val_if_fail (self != NULL, FALSE);
/* XXX: is the empty path closed? Currently it's not */
if (self->last - self->first != 1)
return FALSE;
contour = gsk_path_get_contour (self->path, self->first);
return gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
}
static float
gsk_path_measure_clamp_distance (GskPathMeasure *self,
float distance)
{
if (isnan (distance))
return 0;
return CLAMP (distance, 0, self->length);
}
/**
* gsk_path_measure_get_point:
* @self: a `GskPathMeasure`
* @distance: distance into the path
* @pos: (optional) (out caller-allocates): The coordinates
* of the position at @distance
* @tangent: (optional) (out caller-allocates): The tangent
* to the position at @distance
*
* Calculates the coordinates and tangent of the point @distance
* units into the path. The value will be clamped to the length
* of the path.
*
* If the point is a discontinuous edge in the path, the returned
* point and tangent will describe the line starting at that point
* going forward.
*
* If @self describes an empty path, the returned point will be
* set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
**/
void
gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent)
{
gsize i;
g_return_if_fail (self != NULL);
if (pos == NULL && tangent == NULL)
return;
distance = gsk_path_measure_clamp_distance (self, distance);
for (i = self->first; i < self->last; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->last)
{
/* the empty path goes here */
if (self->first == self->last)
{
if (pos)
graphene_point_init (pos, 0.f, 0.f);
if (tangent)
graphene_vec2_init (tangent, 1.f, 0.f);
return;
}
/* rounding errors can make this happen */
i = self->last - 1;
distance = self->measures[i].length;
}
gsk_contour_get_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
distance,
pos,
tangent);
}
/**
* gsk_path_measure_get_curvature:
* @self: a `GskPathMeasure`
* @distance: distance into the path
* @center: (optional) (out caller-allocates): The center
* of the osculating circle at the point
*
* Calculates the curvature at the point @distance units into
* the path.
*
* Optionally, returns the center of the osculating circle as well.
*
* If the curvature is infinite (at line segments), or does
* not exist (at sharp turns), zero is returned, and @center
* is not modified.
*
* Returns: The curvature of the path at the given point
*/
float
gsk_path_measure_get_curvature (GskPathMeasure *self,
float distance,
graphene_point_t *center)
{
gsize i;
g_return_val_if_fail (self != NULL, 0);
distance = gsk_path_measure_clamp_distance (self, distance);
for (i = self->first; i < self->last; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->last)
{
/* the empty path goes here */
if (self->first == self->last)
return 0;
/* rounding errors can make this happen */
i = self->last - 1;
distance = self->measures[i].length;
}
return gsk_contour_get_curvature (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
distance,
center);
}
/**
* gsk_path_measure_get_closest_point:
* @self: a `GskPathMeasure`
* @point: the point to find the closest point to
* @out_pos: (optional) (out caller-allocates): return location
* for the closest point
*
* Gets the point on the path that is closest to @point.
*
* If the path being measured is empty, return 0 and set
* @out_pos to (0, 0).
*
* This is a simpler and slower version of
* [method@Gsk.PathMeasure.get_closest_point_full].
* Use that one if you need more control.
*
* Returns: The offset into the path of the closest point
**/
float
gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
graphene_point_t *out_pos)
{
float result;
g_return_val_if_fail (self != NULL, 0.0f);
if (gsk_path_measure_get_closest_point_full (self,
point,
INFINITY,
NULL,
out_pos,
&result,
NULL))
return result;
if (out_pos)
*out_pos = GRAPHENE_POINT_INIT (0, 0);
return 0;
}
/**
* gsk_path_measure_get_closest_point_full:
* @self: a `GskPathMeasure`
* @point: the point to find the closest point to
* @threshold: The maximum allowed distance between the path and @point.
* Use INFINITY to look for any point.
* @out_distance: (optional) (out caller-allocates): The
* distance between the found closest point on the path and the given
* @point.
* @out_pos: (optional) (out caller-allocates): return location
* for the closest point
* @out_offset: (optional) (out caller-allocates): The offset into
* the path of the found point
* @out_tangent: (optional) (out caller-allocates): return location for
* the tangent at the closest point
*
* Gets the point on the path that is closest to @point. If no point on
* path is closer to @point than @threshold, return %FALSE.
*
* Returns: %TRUE if a point was found, %FALSE otherwise.
**/
gboolean
gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent)
{
gboolean result;
gsize i;
float distance, length;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (point != NULL, FALSE);
result = FALSE;
length = 0;
for (i = self->first; i < self->last; i++)
{
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
self->tolerance,
point,
threshold,
&distance,
out_pos,
out_offset,
out_tangent))
{
result = TRUE;
if (out_offset)
*out_offset += length;
if (distance < self->tolerance)
break;
threshold = distance - self->tolerance;
}
length += self->measures[i].length;
}
if (result && out_distance)
*out_distance = distance;
return result;
}
/**
* gsk_path_measure_in_fill:
* @self: a #GskPathMeasure
* @point: the point to test
* @fill_rule: the fill rule to follow
*
* Returns whether the given point is inside the area that would be
* affected if the path of @self was filled according to @fill_rule.
*
* Returns: %TRUE if @point is inside
*/
gboolean
gsk_path_measure_in_fill (GskPathMeasure *self,
graphene_point_t *point,
GskFillRule fill_rule)
{
int winding = 0;
gboolean on_edge = FALSE;
int i;
for (i = self->first; i < self->last; i++)
{
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
point,
&on_edge);
if (on_edge)
return TRUE;
}
switch (fill_rule)
{
case GSK_FILL_RULE_EVEN_ODD:
return winding & 1;
case GSK_FILL_RULE_WINDING:
return winding != 0;
default:
g_assert_not_reached ();
}
}
static void
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
GskPathMeasure *measure,
gboolean emit_move_to,
float start,
float end)
{
g_assert (start < end);
for (gsize i = measure->first; i < measure->last; i++)
{
if (measure->measures[i].length < start)
{
start -= measure->measures[i].length;
end -= measure->measures[i].length;
}
else if (start > 0 || end < measure->measures[i].length)
{
float len = MIN (end, measure->measures[i].length);
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
self,
measure->measures[i].contour_data,
emit_move_to,
start,
len);
end -= len;
start = 0;
if (end <= 0)
break;
}
else
{
end -= measure->measures[i].length;
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
}
emit_move_to = TRUE;
}
}
/**
* gsk_path_builder_add_segment:
* @builder: a `GskPathBuilder`
* @measure: the `GskPathMeasure` to take the segment to
* @start: start distance into the path
* @end: end distance into the path
*
* Adds to @builder the segment of @measure from @start to @end.
*
* The distances are given relative to the length of @measure's path,
* from 0 for the beginning of the path to [method@Gsk.PathMeasure.get_length]
* for the end of the path. The values will be clamped to that range.
*
* If @start >= @end after clamping, 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.
**/
void
gsk_path_builder_add_segment (GskPathBuilder *builder,
GskPathMeasure *measure,
float start,
float end)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (measure != NULL);
start = gsk_path_measure_clamp_distance (measure, start);
end = gsk_path_measure_clamp_distance (measure, end);
if (start < end)
{
gsk_path_builder_add_segment_chunk (builder, measure, TRUE, start, end);
}
else
{
/* If the path is closed, we can connect the 2 subpaths. */
gboolean closed = gsk_path_measure_is_closed (measure);
gboolean need_move_to = !closed;
if (start < measure->length)
gsk_path_builder_add_segment_chunk (builder, measure,
TRUE,
start, measure->length);
else
need_move_to = TRUE;
if (end > 0)
gsk_path_builder_add_segment_chunk (builder, measure,
need_move_to,
0, end);
if (start == end && closed)
gsk_path_builder_close (builder);
}
}
-92
View File
@@ -1,92 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_MEASURE_H__
#define __GSK_PATH_MEASURE_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gskpath.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new (GskPath *path);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_unref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_is_closed (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_curvature (GskPathMeasure *self,
float distance,
graphene_point_t *center);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
graphene_point_t *out_pos);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
graphene_point_t *point,
GskFillRule fill_rule);
G_END_DECLS
#endif /* __GSK_PATH_MEASURE_H__ */
-179
View File
@@ -1,179 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATHOP_PRIVATE_H__
#define __GSK_PATHOP_PRIVATE_H__
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
G_BEGIN_DECLS
typedef gpointer gskpathop;
static inline
gskpathop gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts);
static inline
const graphene_point_t *gsk_pathop_points (gskpathop pop);
static inline
GskPathOperation gsk_pathop_op (gskpathop pop);
static inline
gboolean gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data);
/* included inline so tests can use them */
static inline
void gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op);
static inline
void gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op);
/* IMPLEMENTATION */
#define GSK_PATHOP_OPERATION_MASK (0x7)
static inline gskpathop
gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts)
{
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
g_assert ((GPOINTER_TO_SIZE (pts) & GSK_PATHOP_OPERATION_MASK) == 0);
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
}
static inline const graphene_point_t *
gsk_pathop_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
}
static inline
GskPathOperation gsk_pathop_op (gskpathop pop)
{
return GPOINTER_TO_SIZE (pop) & GSK_PATHOP_OPERATION_MASK;
}
static inline gboolean
gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data)
{
switch (gsk_pathop_op (pop))
{
case GSK_PATH_MOVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, 0, user_data);
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
case GSK_PATH_CURVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data);
case GSK_PATH_CONIC:
{
const graphene_point_t *pts = gsk_pathop_points (pop);
return func (gsk_pathop_op (pop), (graphene_point_t[3]) { pts[0], pts[1], pts[3] }, 3, pts[2].x, user_data);
}
default:
g_assert_not_reached ();
return TRUE;
}
}
static inline void
gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (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_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[3].x, pts[3].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
static inline void
gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (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_line_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
G_END_DECLS
#endif /* __GSK_PATHOP_PRIVATE_H__ */
-1427
View File
File diff suppressed because it is too large Load Diff
-78
View File
@@ -1,78 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_PRIVATE_H__
#define __GSK_PATH_PRIVATE_H__
#include "gskpath.h"
#include "gskcontourprivate.h"
#include "gskpathopprivate.h"
G_BEGIN_DECLS
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
GskPath * gsk_path_new_from_contours (const GSList *contours);
gsize gsk_path_get_n_contours (GskPath *path);
const GskContour * gsk_path_get_contour (GskPath *path,
gsize i);
GskPathFlags gsk_path_get_flags (GskPath *self);
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
GskPathForeachFlags flags,
double tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour);
void gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
float rx,
float ry,
float x_axis_rotation,
gboolean large_arc,
gboolean positive_sweep,
float x,
float y);
typedef enum
{
GSK_PATH_OP_SIMPLIFY,
GSK_PATH_OP_UNION,
GSK_PATH_OP_INTERSECTION,
GSK_PATH_OP_DIFFERENCE,
GSK_PATH_OP_XOR
} GskPathOp;
GskPath * gsk_path_op (GskPathOp operation,
GskPath *first,
GskPath *second);
G_END_DECLS
#endif /* __GSK_PATH_PRIVATE_H__ */
-2073
View File
File diff suppressed because it is too large Load Diff
-30
View File
@@ -158,8 +158,6 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
#define GSK_TYPE_CLIP_NODE (gsk_clip_node_get_type())
#define GSK_TYPE_ROUNDED_CLIP_NODE (gsk_rounded_clip_node_get_type())
#define GSK_TYPE_FILL_NODE (gsk_fill_node_get_type())
#define GSK_TYPE_STROKE_NODE (gsk_stroke_node_get_type())
#define GSK_TYPE_SHADOW_NODE (gsk_shadow_node_get_type())
#define GSK_TYPE_BLEND_NODE (gsk_blend_node_get_type())
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
@@ -186,8 +184,6 @@ typedef struct _GskColorMatrixNode GskColorMatrixNode;
typedef struct _GskRepeatNode GskRepeatNode;
typedef struct _GskClipNode GskClipNode;
typedef struct _GskRoundedClipNode GskRoundedClipNode;
typedef struct _GskFillNode GskFillNode;
typedef struct _GskStrokeNode GskStrokeNode;
typedef struct _GskShadowNode GskShadowNode;
typedef struct _GskBlendNode GskBlendNode;
typedef struct _GskCrossFadeNode GskCrossFadeNode;
@@ -449,32 +445,6 @@ GskRenderNode * gsk_rounded_clip_node_get_child (const GskRender
GDK_AVAILABLE_IN_ALL
const GskRoundedRect * gsk_rounded_clip_node_get_clip (const GskRenderNode *node) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
GType gsk_fill_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_fill_node_new (GskRenderNode *child,
GskPath *path,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_fill_node_get_child (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_fill_node_get_path (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskFillRule gsk_fill_node_get_fill_rule (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_stroke_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_stroke_node_new (GskRenderNode *child,
GskPath *path,
const GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_stroke_node_get_child (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_stroke_node_get_path (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskStroke * gsk_stroke_node_get_stroke (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
-373
View File
@@ -23,10 +23,8 @@
#include "gskcairoblurprivate.h"
#include "gskdebugprivate.h"
#include "gskdiffprivate.h"
#include "gskpath.h"
#include "gskrendererprivate.h"
#include "gskroundedrectprivate.h"
#include "gskstrokeprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdktextureprivate.h"
@@ -3763,343 +3761,6 @@ gsk_rounded_clip_node_get_clip (const GskRenderNode *node)
return &self->clip;
}
/*** GSK_FILL_NODE ***/
struct _GskFillNode
{
GskRenderNode render_node;
GskRenderNode *child;
GskPath *path;
GskFillRule fill_rule;
};
static void
gsk_fill_node_finalize (GskRenderNode *node)
{
GskFillNode *self = (GskFillNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
gsk_render_node_unref (self->child);
gsk_path_unref (self->path);
parent_class->finalize (node);
}
static void
gsk_fill_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskFillNode *self = (GskFillNode *) node;
cairo_save (cr);
switch (self->fill_rule)
{
case GSK_FILL_RULE_WINDING:
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
break;
case GSK_FILL_RULE_EVEN_ODD:
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
break;
default:
g_assert_not_reached ();
break;
}
gsk_path_to_cairo (self->path, cr);
cairo_clip (cr);
gsk_render_node_draw (self->child, cr);
cairo_restore (cr);
}
static void
gsk_fill_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskFillNode *self1 = (GskFillNode *) node1;
GskFillNode *self2 = (GskFillNode *) node2;
if (self1->path == self2->path)
{
cairo_region_t *sub;
cairo_rectangle_int_t clip_rect;
graphene_rect_t rect;
sub = cairo_region_create();
gsk_render_node_diff (self1->child, self2->child, sub);
graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
rectangle_init_from_graphene (&clip_rect, &rect);
cairo_region_intersect_rectangle (sub, &clip_rect);
cairo_region_union (region, sub);
cairo_region_destroy (sub);
}
else
{
gsk_render_node_diff_impossible (node1, node2, region);
}
}
/**
* gsk_fill_node_new:
* @child: The node to fill the area with
* @path: The path describing the area to fill
* @fill_rule: The fill rule to use
*
* Creates a `GskRenderNode` that will fill the @child in the area
* given by @path and @fill_rule.
*
* Returns: (transfer none) (type GskFillNode): A new `GskRenderNode`
*/
GskRenderNode *
gsk_fill_node_new (GskRenderNode *child,
GskPath *path,
GskFillRule fill_rule)
{
GskFillNode *self;
GskRenderNode *node;
graphene_rect_t path_bounds;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (path != NULL, NULL);
self = gsk_render_node_alloc (GSK_FILL_NODE);
node = (GskRenderNode *) self;
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
self->fill_rule = fill_rule;
if (gsk_path_get_bounds (path, &path_bounds))
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
else
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
return node;
}
/**
* gsk_fill_node_get_child:
* @node: (type GskFillNode): a fill `GskRenderNode`
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): The child that is getting drawn
**/
GskRenderNode *
gsk_fill_node_get_child (const GskRenderNode *node)
{
const GskFillNode *self = (const GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
return self->child;
}
/**
* gsk_fill_node_get_path:
* @node: (type GskFillNode): a fill `GskRenderNode`
*
* Retrievs the path used to describe the area filled with the contents of
* the @node.
*
* Returns: (transfer none): a `GskPath`
*/
GskPath *
gsk_fill_node_get_path (const GskRenderNode *node)
{
const GskFillNode *self = (const GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
return self->path;
}
/**
* gsk_fill_node_get_fill_rule:
* @node: (type GskFillNode): a fill `GskRenderNode`
*
* Retrievs the fill rule used to determine how the path is filled.
*
* Returns: a `GskFillRule`
*/
GskFillRule
gsk_fill_node_get_fill_rule (const GskRenderNode *node)
{
const GskFillNode *self = (const GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), GSK_FILL_RULE_WINDING);
return self->fill_rule;
}
/*** GSK_STROKE_NODE ***/
struct _GskStrokeNode
{
GskRenderNode render_node;
GskRenderNode *child;
GskPath *path;
GskStroke stroke;
};
static void
gsk_stroke_node_finalize (GskRenderNode *node)
{
GskStrokeNode *self = (GskStrokeNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_STROKE_NODE));
gsk_render_node_unref (self->child);
gsk_path_unref (self->path);
gsk_stroke_clear (&self->stroke);
parent_class->finalize (node);
}
static void
gsk_stroke_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskStrokeNode *self = (GskStrokeNode *) node;
cairo_save (cr);
gsk_cairo_rectangle (cr, &self->child->bounds);
cairo_clip (cr);
cairo_push_group (cr);
gsk_render_node_draw (self->child, cr);
cairo_pop_group_to_source (cr);
gsk_stroke_to_cairo (&self->stroke, cr);
gsk_path_to_cairo (self->path, cr);
cairo_stroke (cr);
cairo_restore (cr);
}
static void
gsk_stroke_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskStrokeNode *self1 = (GskStrokeNode *) node1;
GskStrokeNode *self2 = (GskStrokeNode *) node2;
if (self1->path == self2->path &&
gsk_stroke_equal (&self1->stroke, &self2->stroke))
{
cairo_region_t *sub;
sub = cairo_region_create();
gsk_render_node_diff (self1->child, self2->child, sub);
cairo_region_union (region, sub);
cairo_region_destroy (sub);
}
else
{
gsk_render_node_diff_impossible (node1, node2, region);
}
}
/**
* gsk_stroke_node_new:
* @child: The node to stroke the area with
* @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.
*
* Returns: (transfer none) (type GskStrokeNode): A new `GskRenderNode`
*/
GskRenderNode *
gsk_stroke_node_new (GskRenderNode *child,
GskPath *path,
const GskStroke *stroke)
{
GskStrokeNode *self;
GskRenderNode *node;
graphene_rect_t path_bounds;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (stroke != NULL, NULL);
self = gsk_render_node_alloc (GSK_STROKE_NODE);
node = (GskRenderNode *) self;
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
gsk_stroke_init_copy (&self->stroke, stroke);
if (gsk_path_get_stroke_bounds (path, stroke, &path_bounds))
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
else
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
return node;
}
/**
* gsk_stroke_node_get_child:
* @node: (type GskStrokeNode): a stroke `GskRenderNode`
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): The child that is getting drawn
**/
GskRenderNode *
gsk_stroke_node_get_child (const GskRenderNode *node)
{
const GskStrokeNode *self = (const GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return self->child;
}
/**
* gsk_stroke_node_get_path:
* @node: (type GskStrokeNode): a stroke `GskRenderNode`
*
* Retrievs the path that will be stroked with the contents of
* the @node.
*
* Returns: (transfer none): a `GskPath`
*/
GskPath *
gsk_stroke_node_get_path (const GskRenderNode *node)
{
const GskStrokeNode *self = (const GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return self->path;
}
/**
* gsk_stroke_node_get_stroke:
* @node: (type GskStrokeNode): a stroke `GskRenderNode`
*
* Retrievs the stroke attributes used in this @node.
*
* Returns: a `GskStroke`
*/
const GskStroke *
gsk_stroke_node_get_stroke (const GskRenderNode *node)
{
const GskStrokeNode *self = (const GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return &self->stroke;
}
/*** GSK_SHADOW_NODE ***/
/**
@@ -5622,8 +5283,6 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_matrix_node, GSK_COLOR_MATRIX_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_stroke_node, GSK_STROKE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
@@ -5923,38 +5582,6 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_FILL_NODE,
sizeof (GskFillNode),
NULL,
gsk_fill_node_finalize,
gsk_fill_node_draw,
NULL,
gsk_fill_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskFillNode"), &node_info);
gsk_render_node_types[GSK_FILL_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_STROKE_NODE,
sizeof (GskStrokeNode),
NULL,
gsk_stroke_node_finalize,
gsk_stroke_node_draw,
NULL,
gsk_stroke_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"), &node_info);
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
+6 -354
View File
@@ -23,13 +23,9 @@
#include "gskrendernodeparserprivate.h"
#include "gskpath.h"
#include "gskpathbuilder.h"
#include "gskroundedrectprivate.h"
#include "gskrendernodeprivate.h"
#include "gskstroke.h"
#include "gsktransformprivate.h"
#include "gskenumtypes.h"
#include "gdk/gdkrgbaprivate.h"
#include "gdk/gdktextureprivate.h"
@@ -403,10 +399,7 @@ parse_string (GtkCssParser *parser,
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
{
gtk_css_parser_error_syntax (parser, "Expected a string");
return FALSE;
}
return FALSE;
s = g_strdup (token->string.string);
gtk_css_parser_consume_token (parser);
@@ -962,26 +955,6 @@ create_default_render_node (void)
return gsk_color_node_new (&GDK_RGBA("FF00CC"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
}
static GskPath *
create_default_path (void)
{
GskPathBuilder *builder;
guint i;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 25, 0);
for (i = 1; i < 5; i++)
{
gsk_path_builder_line_to (builder,
sin (i * G_PI * 0.8) * 25 + 25,
-cos (i * G_PI * 0.8) * 25 + 25);
}
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static GskRenderNode *
parse_color_node (GtkCssParser *parser)
{
@@ -1129,7 +1102,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
@@ -1415,7 +1388,7 @@ parse_cairo_node (GtkCssParser *parser)
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
node = gsk_cairo_node_new (&bounds);
if (surface != NULL)
{
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
@@ -1792,208 +1765,6 @@ parse_rounded_clip_node (GtkCssParser *parser)
return result;
}
static gboolean
parse_path (GtkCssParser *parser,
gpointer out_path)
{
GskPath *path;
char *str = NULL;
if (!parse_string (parser, &str))
return FALSE;
path = gsk_path_parse (str);
g_free (str);
if (path == NULL)
{
gtk_css_parser_error_value (parser, "Invalid path");
return FALSE;
}
*((GskPath **) out_path) = path;
return TRUE;
}
static void
clear_path (gpointer inout_path)
{
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
}
static gboolean
parse_dash (GtkCssParser *parser,
gpointer out_dash)
{
GArray *dash;
double d;
/* because CSS does this, too */
if (gtk_css_parser_try_ident (parser, "none"))
{
*((GArray **) out_dash) = NULL;
return TRUE;
}
dash = g_array_new (FALSE, FALSE, sizeof (float));
do {
if (!gtk_css_parser_consume_number (parser, &d))
{
g_array_free (dash, TRUE);
return FALSE;
}
g_array_append_vals (dash, (float[1]) { d }, 1);
} while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER));
*((GArray **) out_dash) = dash;
return TRUE;
}
static void
clear_dash (gpointer inout_array)
{
g_clear_pointer ((GArray **) inout_array, g_array_unref);
}
static gboolean
parse_enum (GtkCssParser *parser,
GType type,
gpointer out_value)
{
GEnumClass *class;
GEnumValue *v;
const GtkCssToken *token;
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
{
gtk_css_parser_error_syntax (parser, "Expected a valid identifier");
return FALSE;
}
class = g_type_class_ref (type);
v = g_enum_get_value_by_nick (class, token->string.string);
if (v == NULL)
{
gtk_css_parser_error_value (parser, "\"%s\" is not a valid identifier here", token->string.string);
g_type_class_unref (class);
return FALSE;
}
*(int*)out_value = v->value;
g_type_class_unref (class);
gtk_css_parser_consume_token (parser);
return TRUE;
}
static gboolean
parse_fill_rule (GtkCssParser *parser,
gpointer out_rule)
{
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
}
static GskRenderNode *
parse_fill_node (GtkCssParser *parser)
{
GskRenderNode *child = NULL;
GskPath *path = NULL;
int rule = GSK_FILL_RULE_WINDING;
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "path", parse_path, clear_path, &path },
{ "fill-rule", parse_fill_rule, NULL, &rule },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
if (child == NULL)
child = create_default_render_node ();
if (path == NULL)
path = create_default_path ();
result = gsk_fill_node_new (child, path, rule);
gsk_path_unref (path);
gsk_render_node_unref (child);
return result;
}
static gboolean
parse_line_cap (GtkCssParser *parser,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
}
static gboolean
parse_line_join (GtkCssParser *parser,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
}
static GskRenderNode *
parse_stroke_node (GtkCssParser *parser)
{
GskRenderNode *child = NULL;
GskPath *path = NULL;
double line_width = 1.0;
int line_cap = GSK_LINE_CAP_BUTT;
int line_join = GSK_LINE_JOIN_MITER;
double miter_limit = 4.0;
GArray *dash = NULL;
double dash_offset = 0.0;
GskStroke *stroke;
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "path", parse_path, clear_path, &path },
{ "line-width", parse_double, NULL, &line_width },
{ "line-cap", parse_line_cap, NULL, &line_cap },
{ "line-join", parse_line_join, NULL, &line_join },
{ "miter-limit", parse_double, NULL, &miter_limit },
{ "dash", parse_dash, clear_dash, &dash },
{ "dash-offset", parse_double, NULL, &dash_offset},
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
if (child == NULL)
child = create_default_render_node ();
if (path == NULL)
path = create_default_path ();
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 (dash)
{
gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
g_array_free (dash, TRUE);
}
gsk_stroke_set_dash_offset (stroke, dash_offset);
result = gsk_stroke_node_new (child, path, stroke);
gsk_path_unref (path);
gsk_stroke_free (stroke);
gsk_render_node_unref (child);
return result;
}
static GskRenderNode *
parse_shadow_node (GtkCssParser *parser)
{
@@ -2063,7 +1834,6 @@ parse_node (GtkCssParser *parser,
{ "container", parse_container_node },
{ "cross-fade", parse_cross_fade_node },
{ "debug", parse_debug_node },
{ "fill", parse_fill_node },
{ "inset-shadow", parse_inset_shadow_node },
{ "linear-gradient", parse_linear_gradient_node },
{ "radial-gradient", parse_radial_gradient_node },
@@ -2075,7 +1845,6 @@ parse_node (GtkCssParser *parser,
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
{ "rounded-clip", parse_rounded_clip_node },
{ "shadow", parse_shadow_node },
{ "stroke", parse_stroke_node },
{ "text", parse_text_node },
{ "texture", parse_texture_node },
{ "transform", parse_transform_node },
@@ -2331,7 +2100,7 @@ append_float_param (Printer *p,
float value,
float default_value)
{
/* Don't approximate-compare here, better be too verbose */
/* Don't approximate-compare here, better be topo verbose */
if (value == default_value)
return;
@@ -2506,11 +2275,8 @@ append_escaping_newlines (GString *str,
len = strcspn (string, "\n");
g_string_append_len (str, string, len);
string += len;
if (*string)
{
g_string_append (str, "\\\n");
string++;
}
g_string_append (str, "\\\n");
string++;
} while (*string);
}
@@ -2625,83 +2391,6 @@ gsk_text_node_serialize_glyphs (GskRenderNode *node,
pango_glyph_string_free (ascii);
}
static const char *
enum_to_nick (GType type,
int value)
{
GEnumClass *class;
GEnumValue *v;
class = g_type_class_ref (type);
v = g_enum_get_value (class, value);
g_type_class_unref (class);
return v->value_nick;
}
static void
append_enum_param (Printer *p,
const char *param_name,
GType type,
int value)
{
_indent (p);
g_string_append_printf (p->str, "%s: ", param_name);
g_string_append (p->str, enum_to_nick (type, value));
g_string_append_c (p->str, ';');
g_string_append_c (p->str, '\n');
}
static void
append_path_param (Printer *p,
const char *param_name,
GskPath *path)
{
char *str, *s;
_indent (p);
g_string_append (p->str, "path: \"\\\n");
str = gsk_path_to_string (path);
/* Put each command on a new line */
for (s = str; *s; s++)
{
if (*s == ' ' &&
(s[1] == 'M' || s[1] == 'C' || s[1] == 'Z' || s[1] == 'L' || s[1] == 'O'))
*s = '\n';
}
append_escaping_newlines (p->str, str);
g_string_append (p->str, "\";\n");
g_free (str);
}
static void
append_dash_param (Printer *p,
const char *param_name,
const float *dash,
gsize n_dash)
{
_indent (p);
g_string_append (p->str, "dash: ");
if (n_dash == 0)
{
g_string_append (p->str, "none");
}
else
{
gsize i;
string_append_double (p->str, dash[0]);
for (i = 1; i < n_dash; i++)
{
g_string_append_c (p->str, ' ');
string_append_double (p->str, dash[i]);
}
}
g_string_append (p->str, ";\n");
}
static void
render_node_print (Printer *p,
GskRenderNode *node)
@@ -2850,42 +2539,6 @@ render_node_print (Printer *p,
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
end_node (p);
}
break;
case GSK_FILL_NODE:
{
start_node (p, "fill");
append_node_param (p, "child", gsk_fill_node_get_child (node));
append_path_param (p, "path", gsk_fill_node_get_path (node));
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
end_node (p);
}
break;
case GSK_STROKE_NODE:
{
const GskStroke *stroke;
const float *dash;
gsize n_dash;
start_node (p, "stroke");
append_node_param (p, "child", gsk_stroke_node_get_child (node));
append_path_param (p, "path", gsk_stroke_node_get_path (node));
stroke = gsk_stroke_node_get_stroke (node);
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0f);
append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
append_float_param (p, "miter-limit", gsk_stroke_get_miter_limit (stroke), 4.0f);
dash = gsk_stroke_get_dash (stroke, &n_dash);
if (dash)
append_dash_param (p, "dash", dash, n_dash);
append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
end_node (p);
}
@@ -3392,4 +3045,3 @@ gsk_render_node_serialize (GskRenderNode *node)
return g_string_free_to_bytes (p.str);
}
-208
View File
@@ -1,208 +0,0 @@
/*
* Copyright © 2002 University of Southern California
* 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
* Carl D. Worth <cworth@cworth.org>
*/
#include "config.h"
#include "gsksplineprivate.h"
#include <math.h>
/* Spline deviation from the circle in radius would be given by:
error = sqrt (x**2 + y**2) - 1
A simpler error function to work with is:
e = x**2 + y**2 - 1
From "Good approximation of circles by curvature-continuous Bezier
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
Design 8 (1990) 22-41, we learn:
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
and
abs (error) =~ 1/2 * e
Of course, this error value applies only for the particular spline
approximation that is used in _cairo_gstate_arc_segment.
*/
static float
arc_error_normalized (float angle)
{
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
}
static float
arc_max_angle_for_tolerance_normalized (float tolerance)
{
float angle, error;
guint i;
/* Use table lookup to reduce search time in most cases. */
struct {
float angle;
float error;
} table[] = {
{ G_PI / 1.0, 0.0185185185185185036127 },
{ G_PI / 2.0, 0.000272567143730179811158 },
{ G_PI / 3.0, 2.38647043651461047433e-05 },
{ G_PI / 4.0, 4.2455377443222443279e-06 },
{ G_PI / 5.0, 1.11281001494389081528e-06 },
{ G_PI / 6.0, 3.72662000942734705475e-07 },
{ G_PI / 7.0, 1.47783685574284411325e-07 },
{ G_PI / 8.0, 6.63240432022601149057e-08 },
{ G_PI / 9.0, 3.2715520137536980553e-08 },
{ G_PI / 10.0, 1.73863223499021216974e-08 },
{ G_PI / 11.0, 9.81410988043554039085e-09 },
};
for (i = 0; i < G_N_ELEMENTS (table); i++)
{
if (table[i].error < tolerance)
return table[i].angle;
}
i++;
do {
angle = G_PI / i++;
error = arc_error_normalized (angle);
} while (error > tolerance);
return angle;
}
static guint
arc_segments_needed (float angle,
float radius,
float tolerance)
{
float max_angle;
/* the error is amplified by at most the length of the
* major axis of the circle; see cairo-pen.c for a more detailed analysis
* of this. */
max_angle = arc_max_angle_for_tolerance_normalized (tolerance / radius);
return ceil (fabs (angle) / max_angle);
}
/* We want to draw a single spline approximating a circular arc radius
R from angle A to angle B. Since we want a symmetric spline that
matches the endpoints of the arc in position and slope, we know
that the spline control points must be:
(R * cos(A), R * sin(A))
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
(R * cos(B), R * sin(B))
for some value of h.
"Approximation of circular arcs by cubic polynomials", Michael
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
various values of h along with error analysis for each.
From that paper, a very practical value of h is:
h = 4/3 * R * tan(angle/4)
This value does not give the spline with minimal error, but it does
provide a very good approximation, (6th-order convergence), and the
error expression is quite simple, (see the comment for
_arc_error_normalized).
*/
static gboolean
gsk_spline_decompose_arc_segment (const graphene_point_t *center,
float radius,
float angle_A,
float angle_B,
GskSplineAddCurveFunc curve_func,
gpointer user_data)
{
float r_sin_A, r_cos_A;
float r_sin_B, r_cos_B;
float h;
r_sin_A = radius * sin (angle_A);
r_cos_A = radius * cos (angle_A);
r_sin_B = radius * sin (angle_B);
r_cos_B = radius * cos (angle_B);
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
return curve_func ((graphene_point_t[4]) {
GRAPHENE_POINT_INIT (
center->x + r_cos_A,
center->y + r_sin_A
),
GRAPHENE_POINT_INIT (
center->x + r_cos_A - h * r_sin_A,
center->y + r_sin_A + h * r_cos_A
),
GRAPHENE_POINT_INIT (
center->x + r_cos_B + h * r_sin_B,
center->y + r_sin_B - h * r_cos_B
),
GRAPHENE_POINT_INIT (
center->x + r_cos_B,
center->y + r_sin_B
)
},
user_data);
}
gboolean
gsk_spline_decompose_arc (const graphene_point_t *center,
float radius,
float tolerance,
float start_angle,
float end_angle,
GskSplineAddCurveFunc curve_func,
gpointer user_data)
{
float step = start_angle - end_angle;
guint i, n_segments;
/* Recurse if drawing arc larger than pi */
if (ABS (step) > G_PI)
{
float mid_angle = (start_angle + end_angle) / 2.0;
return gsk_spline_decompose_arc (center, radius, tolerance, start_angle, mid_angle, curve_func, user_data)
&& gsk_spline_decompose_arc (center, radius, tolerance, mid_angle, end_angle, curve_func, user_data);
}
else if (ABS (step) < tolerance)
{
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
}
n_segments = arc_segments_needed (ABS (step), radius, tolerance);
step = (end_angle - start_angle) / n_segments;
for (i = 0; i < n_segments - 1; i++, start_angle += step)
{
if (!gsk_spline_decompose_arc_segment (center, radius, start_angle, start_angle + step, curve_func, user_data))
return FALSE;
}
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
}
-41
View File
@@ -1,41 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_SPLINE_PRIVATE_H__
#define __GSK_SPLINE_PRIVATE_H__
#include "gskpath.h"
G_BEGIN_DECLS
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
gpointer user_data);
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
float radius,
float tolerance,
float start_angle,
float end_angle,
GskSplineAddCurveFunc curve_func,
gpointer user_data);
G_END_DECLS
#endif /* __GSK_SPLINE_PRIVATE_H__ */
-458
View File
@@ -1,458 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskstrokeprivate.h"
/**
* GskStroke:
*
* A `GskStroke` describes the attributes that are used when stroking a path.
*/
G_DEFINE_BOXED_TYPE (GskStroke, gsk_stroke,
gsk_stroke_copy,
gsk_stroke_free)
/**
* gsk_stroke_new:
* @line_width: line width of the stroke. Must be > 0
*
* Creates a new `GskStroke` with the given @line_width.
*
* Returns: a new `GskStroke`
**/
GskStroke *
gsk_stroke_new (float line_width)
{
GskStroke *self;
g_return_val_if_fail (line_width > 0, NULL);
self = g_new0 (GskStroke, 1);
self->line_width = line_width;
self->line_cap = GSK_LINE_CAP_BUTT;
self->line_join = GSK_LINE_JOIN_MITER;
self->miter_limit = 4.f; /* following svg */
return self;
}
/**
* gsk_stroke_copy:
* @other: `GskStroke` to copy
*
* Creates a copy of the given @other stroke.
*
* Returns: a new `GskStroke`. Use [method@Gsk.Stroke.free] to free it.
**/
GskStroke *
gsk_stroke_copy (const GskStroke *other)
{
GskStroke *self;
g_return_val_if_fail (other != NULL, NULL);
self = g_new (GskStroke, 1);
gsk_stroke_init_copy (self, other);
return self;
}
/**
* gsk_stroke_free:
* @self: a `GskStroke`
*
* Frees a `GskStroke`.
**/
void
gsk_stroke_free (GskStroke *self)
{
if (self == NULL)
return;
gsk_stroke_clear (self);
g_free (self);
}
/**
* gsk_stroke_to_cairo:
* @self: a #GskStroke
* @cr: the cairo context to configure
*
* A helper function that sets the stroke parameters
* of @cr from the values found in @self.
*/
void
gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr)
{
cairo_set_line_width (cr, self->line_width);
/* gcc can optimize that to a direct case. This catches later additions to the enum */
switch (self->line_cap)
{
case GSK_LINE_CAP_BUTT:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
break;
case GSK_LINE_CAP_ROUND:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
break;
case GSK_LINE_CAP_SQUARE:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
break;
default:
g_assert_not_reached ();
break;
}
/* gcc can optimize that to a direct case. This catches later additions to the enum */
switch (self->line_join)
{
case GSK_LINE_JOIN_MITER:
case GSK_LINE_JOIN_MITER_CLIP:
case GSK_LINE_JOIN_ARCS:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
break;
case GSK_LINE_JOIN_ROUND:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
break;
case GSK_LINE_JOIN_BEVEL:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
break;
default:
g_assert_not_reached ();
break;
}
cairo_set_miter_limit (cr, self->miter_limit);
if (self->dash_length)
{
gsize i;
double *dash = g_newa (double, self->n_dash);
for (i = 0; i < self->n_dash; i++)
{
dash[i] = self->dash[i];
}
cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
}
else
cairo_set_dash (cr, NULL, 0, 0.0);
}
/**
* gsk_stroke_equal:
* @stroke1: the first `GskStroke`
* @stroke2: the second `GskStroke`
*
* Checks if two strokes are identical.
*
* Returns: %TRUE if the two strokes are equal, %FALSE otherwise
**/
gboolean
gsk_stroke_equal (gconstpointer stroke1,
gconstpointer stroke2)
{
const GskStroke *self1 = stroke1;
const GskStroke *self2 = stroke2;
if (self1->line_width != self2->line_width ||
self1->line_cap != self2->line_cap ||
self1->line_join != self2->line_join ||
self1->miter_limit != self2->miter_limit)
return FALSE;
if (self1->n_dash != self2->n_dash)
return FALSE;
for (int i = 0; i < self1->n_dash; i++)
{
if (self1->dash[i] != self2->dash[i])
return FALSE;
}
if (self1->dash_offset != self2->dash_offset)
return FALSE;
return TRUE;
}
/**
* gsk_stroke_set_line_width:
* @self: a `GskStroke`
* @line_width: width of the line in pixels
*
* Sets the line width to be used when stroking.
*
* The line width must be > 0.
**/
void
gsk_stroke_set_line_width (GskStroke *self,
float line_width)
{
g_return_if_fail (self != NULL);
g_return_if_fail (line_width > 0);
self->line_width = line_width;
}
/**
* gsk_stroke_get_line_width:
* @self: a `GskStroke`
*
* Gets the line width used.
*
* Returns: The line width
**/
float
gsk_stroke_get_line_width (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_width;
}
/**
* gsk_stroke_set_line_cap:
* @self: a `GskStroke`
* @line_cap: the `GskLineCap`
*
* Sets the line cap to be used when stroking.
*
* See `GskLineCap` for details.
**/
void
gsk_stroke_set_line_cap (GskStroke *self,
GskLineCap line_cap)
{
g_return_if_fail (self != NULL);
self->line_cap = line_cap;
}
/**
* gsk_stroke_get_line_cap:
* @self: a `GskStroke`
*
* Gets the line cap used.
*
* See `GskLineCap` for details.
*
* Returns: The line cap
**/
GskLineCap
gsk_stroke_get_line_cap (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_cap;
}
/**
* gsk_stroke_set_line_join:
* @self: a `GskStroke`
* @line_join: The line join to use
*
* Sets the line join to be used when stroking.
*
* See `GskLineJoin` for details.
**/
void
gsk_stroke_set_line_join (GskStroke *self,
GskLineJoin line_join)
{
g_return_if_fail (self != NULL);
self->line_join = line_join;
}
/**
* gsk_stroke_get_line_join:
* @self: a `GskStroke`
*
* Gets the line join used.
*
* See `GskLineJoin` for details.
*
* Returns: The line join
**/
GskLineJoin
gsk_stroke_get_line_join (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_join;
}
/**
* gsk_stroke_set_miter_limit:
* @self: a `GskStroke`
* @limit: the miter limit, must be non-negative
*
* Sets the limit for the distance from the corner where sharp
* turns of joins get cut off. The miter limit is in units of
* line width.
*
* For joins of type `GSK_LINE_JOIN_MITER` that exceed the miter
* limit, the join gets rendered as if it was of type
* `GSK_LINE_JOIN_BEVEL`. For joins of type `GSK_LINE_JOIN_MITER_CLIP`,
* the miter is clipped at a distance of half the miter limit.
*/
void
gsk_stroke_set_miter_limit (GskStroke *self,
float limit)
{
g_return_if_fail (self != NULL);
g_return_if_fail (limit >= 0);
self->miter_limit = limit;
}
/**
* gsk_stroke_get_miter_limit:
* @self: a `GskStroke`
*
* Returns the miter limit of a `GskStroke`.
*/
float
gsk_stroke_get_miter_limit (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 4.f);
return self->miter_limit;
}
/**
* gsk_stroke_set_dash:
* @self: a `GskStroke`
* @dash: (array length=n_dash) (transfer none) (allow-none): the array of dashes
* @n_dash: number of elements in @dash
*
* Sets the dash pattern to use by this stroke. A dash pattern is specified by
* an array of alternating non-negative values. Each value provides the length
* of alternate "on" and "off" portions of the stroke.
*
* Each "on" segment will have caps applied as if the segment were a separate
* contour. In particular, it is valid to use an "on" length of 0 with
* `GSK_LINE_CAP_ROUND` or `GSK_LINE_CAP_SQUARE` to draw dots or squares along
* a path.
*
* If @n_dash is 0, if all elements in @dash are 0, or if there are negative
* values in @dash, then dashing is disabled.
*
* If @n_dash is 1, an alternating "on" and "off" pattern with the single
* dash length provided is assumed.
*
* If @n_dash is uneven, the dash array will be used with the first element
* in @dash defining an "on" or "off" in alternating passes through the array.
*
* You can specify a starting offset into the dash with [method@Gsk.Stroke.set_dash_offset].
**/
void
gsk_stroke_set_dash (GskStroke *self,
const float *dash,
gsize n_dash)
{
float dash_length;
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (dash != NULL || n_dash == 0);
dash_length = 0;
for (i = 0; i < n_dash; i++)
{
if (!(dash[i] >= 0)) /* should catch NaN */
{
g_critical ("invalid value in dash array at position %zu", i);
return;
}
dash_length += dash[i];
}
self->dash_length = dash_length;
g_free (self->dash);
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
self->n_dash = n_dash;
}
/**
* gsk_stroke_get_dash:
* @self: a `GskStroke`
* @n_dash: (out caller-allocates): number of elements
* in the array returned
*
* Gets the dash array in use or %NULL if dashing is disabled.
*
* Returns: (array length=n_dash) (transfer none) (allow-none):
* The dash array or %NULL if the dash array is empty.
**/
const float *
gsk_stroke_get_dash (const GskStroke *self,
gsize *n_dash)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (n_dash != NULL, NULL);
*n_dash = self->n_dash;
return self->dash;
}
/**
* gsk_stroke_set_dash_offset:
* @self: a `GskStroke`
* @offset: offset into the dash pattern
*
* Sets the offset into the dash pattern set via [method@Gsk.Stroke.set_dash]
* where dashing should begin.
*
* This is an offset into the length of the path, not an index into
* the array values of the dash array.
**/
void
gsk_stroke_set_dash_offset (GskStroke *self,
float offset)
{
g_return_if_fail (self != NULL);
self->dash_offset = offset;
}
/**
* gsk_stroke_get_dash_offset:
* @self: a `GskStroke`
*
* Returns the dash offset of a `GskStroke`.
*/
float
gsk_stroke_get_dash_offset (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 4.f);
return self->dash_offset;
}
-91
View File
@@ -1,91 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_STROKE_H__
#define __GSK_STROKE_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_STROKE (gsk_stroke_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_stroke_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskStroke * gsk_stroke_new (float line_width);
GDK_AVAILABLE_IN_ALL
GskStroke * gsk_stroke_copy (const GskStroke *other);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_free (GskStroke *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_stroke_equal (gconstpointer stroke1,
gconstpointer stroke2);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_width (GskStroke *self,
float line_width);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_line_width (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_cap (GskStroke *self,
GskLineCap line_cap);
GDK_AVAILABLE_IN_ALL
GskLineCap gsk_stroke_get_line_cap (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_join (GskStroke *self,
GskLineJoin line_join);
GDK_AVAILABLE_IN_ALL
GskLineJoin gsk_stroke_get_line_join (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_miter_limit (GskStroke *self,
float limit);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_miter_limit (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_dash (GskStroke *self,
const float *dash,
gsize n_dash);
GDK_AVAILABLE_IN_ALL
const float * gsk_stroke_get_dash (const GskStroke *self,
gsize *n_dash);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_dash_offset (GskStroke *self,
float offset);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_dash_offset (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr);
G_END_DECLS
#endif /* __GSK_STROKE_H__ */
-60
View File
@@ -1,60 +0,0 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_STROKE_PRIVATE_H__
#define __GSK_STROKE_PRIVATE_H__
#include "gskstroke.h"
G_BEGIN_DECLS
struct _GskStroke
{
float line_width;
GskLineCap line_cap;
GskLineJoin line_join;
float miter_limit;
float *dash;
gsize n_dash;
float dash_length; /* sum of all dashes in the array */
float dash_offset;
};
static inline void
gsk_stroke_init_copy (GskStroke *stroke,
const GskStroke *other)
{
*stroke = *other;
stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
}
static inline void
gsk_stroke_clear (GskStroke *stroke)
{
g_clear_pointer (&stroke->dash, g_free);
stroke->n_dash = 0; /* better safe than sorry */
}
G_END_DECLS
#endif /* __GSK_STROKE_PRIVATE_H__ */
-4
View File
@@ -26,11 +26,7 @@
#include <gdk/gdk.h>
#include <gsk/gskenums.h>
typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskStroke GskStroke;
typedef struct _GskTransform GskTransform;
#endif /* __GSK_TYPES_H__ */
+1 -19
View File
@@ -22,41 +22,27 @@ gsk_private_gl_shaders = [
]
gsk_public_sources = files([
'gskcairorenderer.c',
'gskdiff.c',
'gskcairorenderer.c',
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
'gskpathdash.c',
'gskpathmeasure.c',
'gskpathops.c',
'gskpathstroke.c',
'gskrenderer.c',
'gskrendernode.c',
'gskrendernodeimpl.c',
'gskrendernodeparser.c',
'gskroundedrect.c',
'gskstroke.c',
'gsktransform.c',
'gl/gskglrenderer.c',
])
gsk_private_sources = files([
'gskboundingbox.c',
'gskcairoblur.c',
'gskcontour.c',
'gskconvexity.c',
'gskcurve.c',
'gskcurveintersect.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gskspline.c',
'gl/gskglattachmentstate.c',
'gl/gskglbuffer.c',
'gl/gskglcommandqueue.c',
'gl/gskglcompiler.c',
'gl/gskglprofiler.c',
'gl/gskgldriver.c',
'gl/gskglglyphlibrary.c',
'gl/gskgliconlibrary.c',
@@ -79,13 +65,9 @@ gsk_public_headers = files([
'gskcairorenderer.h',
'gskenums.h',
'gskglshader.h',
'gskpath.h',
'gskpathbuilder.h',
'gskpathmeasure.h',
'gskrenderer.h',
'gskrendernode.h',
'gskroundedrect.h',
'gskstroke.h',
'gsktransform.h',
'gsktypes.h',
'gsk-autocleanup.h',
-2
View File
@@ -260,8 +260,6 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
+1 -1
View File
@@ -66,7 +66,7 @@ for f in get_files('theme/Default/assets-hc', '.svg'):
xml += ' <file preprocess=\'xml-stripblanks\'>theme/Default/assets-hc/{0}</file>\n'.format(f)
for f in get_files('ui', '.ui'):
xml += ' <file>ui/{0}</file>\n'.format(f)
xml += ' <file alias="ui/{0}">ui/{0}.precompiled</file>\n'.format(f)
xml += '\n'
+16 -11
View File
@@ -945,6 +945,7 @@ gtk_action_muxer_register_observer (GtkActionObservable *observable,
gboolean enabled;
const GVariantType *parameter_type;
GVariant *state;
gboolean is_duplicate;
if (!muxer->observed_actions)
muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_action);
@@ -961,22 +962,26 @@ gtk_action_muxer_register_observer (GtkActionObservable *observable,
g_hash_table_insert (muxer->observed_actions, action->fullname, action);
}
is_duplicate = g_slist_find (action->watchers, observer) != NULL;
action->watchers = g_slist_prepend (action->watchers, observer);
g_object_weak_ref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action);
if (action_muxer_query_action (muxer, name,
&enabled, &parameter_type,
NULL, NULL, &state, TRUE))
if (!is_duplicate)
{
gtk_action_muxer_action_added (muxer, name, parameter_type, enabled, state);
g_clear_pointer (&state, g_variant_unref);
}
if (action_muxer_query_action (muxer, name,
&enabled, &parameter_type,
NULL, NULL, &state, TRUE))
{
gtk_action_muxer_action_added (muxer, name, parameter_type, enabled, state);
g_clear_pointer (&state, g_variant_unref);
}
if (muxer->parent)
{
gtk_action_observable_register_observer (GTK_ACTION_OBSERVABLE (muxer->parent),
name,
GTK_ACTION_OBSERVER (muxer));
if (muxer->parent)
{
gtk_action_observable_register_observer (GTK_ACTION_OBSERVABLE (muxer->parent),
name,
GTK_ACTION_OBSERVER (muxer));
}
}
}
+335 -151
View File
@@ -27,100 +27,228 @@
typedef enum
{
RECORD_TYPE_ELEMENT,
RECORD_TYPE_END_ELEMENT,
RECORD_TYPE_TEXT,
} RecordTreeType;
RECORD_TYPE_ELEMENT,
RECORD_TYPE_END_ELEMENT,
RECORD_TYPE_TEXT,
} RecordDataType;
typedef struct RecordDataTree RecordDataTree;
/* All strings are owned by the string chunk */
typedef struct {
/* Must be first for g_slice_free_chain() */
GList link;
/* All strings are owned by the string table */
struct RecordDataTree {
RecordDataTree *parent;
RecordTreeType type;
const char *string;
int len;
int count;
int offset;
int text_offset;
gboolean include_len;
} RecordDataString;
typedef struct {
RecordDataType type;
GList link;
} RecordDataNode;
typedef struct RecordDataElement RecordDataElement;
struct RecordDataElement {
RecordDataNode base;
RecordDataElement *parent;
GQueue children;
int n_attributes;
const char *data;
const char **attributes;
const char **values;
GList *children;
gboolean preserve_whitespace;
RecordDataString *name;
RecordDataString *attributes[];
};
typedef struct {
char *string;
int count;
int offset;
} RecordDataString;
RecordDataNode base;
static RecordDataTree *
record_data_tree_new (RecordDataTree *parent,
RecordTreeType type,
const char *data)
{
RecordDataTree *tree = g_slice_new0 (RecordDataTree);
tree->parent = parent;
tree->type = type;
tree->data = data;
if (parent)
parent->children = g_list_prepend (parent->children, tree);
return tree;
}
static void
record_data_tree_free (RecordDataTree *tree)
{
g_list_free_full (tree->children, (GDestroyNotify)record_data_tree_free);
g_free (tree->attributes);
g_free (tree->values);
g_slice_free (RecordDataTree, tree);
}
static void
record_data_string_free (RecordDataString *s)
{
g_free (s->string);
g_slice_free (RecordDataString, s);
}
static const char *
record_data_string_lookup (GHashTable *strings,
const char *str,
gssize len)
{
char *copy = NULL;
RecordDataString *s;
if (len >= 0)
{
/* Ensure str is zero terminated */
copy = g_strndup (str, len);
str = copy;
}
s = g_hash_table_lookup (strings, str);
if (s)
{
g_free (copy);
s->count++;
return s->string;
}
s = g_slice_new (RecordDataString);
s->string = copy ? copy : g_strdup (str);
s->count = 1;
g_hash_table_insert (strings, s->string, s);
return s->string;
}
RecordDataString *string;
} RecordDataText;
typedef struct {
GHashTable *strings;
RecordDataTree *root;
RecordDataTree *current;
GStringChunk *chunks;
GQueue string_list;
RecordDataElement *root;
RecordDataElement *current;
} RecordData;
static gpointer
record_data_node_new (RecordDataElement *parent,
RecordDataType type,
gsize size)
{
RecordDataNode *node = g_slice_alloc0 (size);
node->type = type;
node->link.data = node;
if (parent)
g_queue_push_tail_link (&parent->children, &node->link);
return node;
}
static gboolean
text_is_important (const char *name)
{
const char *elements[] = {
"property",
"attribute",
"col",
"action-widget",
"item",
"mime-type",
"pattern",
"suffix",
"mark",
NULL
};
return g_strv_contains (elements, name);
}
static RecordDataElement *
record_data_element_new (RecordDataElement *parent,
RecordDataString *name,
gsize n_attributes)
{
RecordDataElement *element;
element = record_data_node_new (parent,
RECORD_TYPE_ELEMENT,
sizeof (RecordDataElement) +
sizeof (RecordDataString) * n_attributes);
element->parent = parent;
element->name = name;
element->preserve_whitespace = name && text_is_important (name->string);
element->n_attributes = n_attributes;
return element;
}
static void
record_data_element_append_text (RecordDataElement *parent,
RecordDataString *string)
{
RecordDataText *text;
text = record_data_node_new (parent,
RECORD_TYPE_TEXT,
sizeof (RecordDataText));
text->string = string;
}
static void
record_data_node_free (RecordDataNode *node)
{
GList *l, *next;
RecordDataText *text;
RecordDataElement *element;
switch (node->type)
{
case RECORD_TYPE_ELEMENT:
element = (RecordDataElement *)node;
l = element->children.head;
while (l)
{
next = l->next;
record_data_node_free (l->data);
l = next;
}
g_slice_free1 (sizeof (RecordDataElement) +
sizeof (RecordDataString) * element->n_attributes, element);
break;
case RECORD_TYPE_TEXT:
text = (RecordDataText *)node;
g_slice_free (RecordDataText, text);
break;
case RECORD_TYPE_END_ELEMENT:
default:
g_assert_not_reached ();
}
}
static gboolean
record_data_string_equal (gconstpointer _a,
gconstpointer _b)
{
const RecordDataString *a = _a;
const RecordDataString *b = _b;
return a->len == b->len &&
memcmp (a->string, b->string, a->len) == 0;
}
/* Copied from g_bytes_hash() */
static guint
record_data_string_hash (gconstpointer _a)
{
const RecordDataString *a = _a;
const signed char *p, *e;
guint32 h = 5381;
for (p = (signed char *)a->string, e = (signed char *)a->string + a->len; p != e; p++)
h = (h << 5) + h + *p;
return h;
}
static int
record_data_string_compare (gconstpointer _a,
gconstpointer _b,
gpointer user_data)
{
const RecordDataString *a = _a;
const RecordDataString *b = _b;
return b->count - a->count;
}
static RecordDataString *
record_data_string_lookup (RecordData *data,
const char *str,
gssize len)
{
RecordDataString *s, tmp;
gboolean include_len = len >= 0;
if (len < 0)
len = strlen (str);
tmp.string = str;
tmp.len = len;
s = g_hash_table_lookup (data->strings, &tmp);
if (s)
{
s->count++;
s->include_len |= include_len;
return s;
}
s = g_slice_new (RecordDataString);
/* The string is zero terminated */
s->string = g_string_chunk_insert_len (data->chunks, str, len);
s->len = len;
s->count = 1;
s->include_len = include_len;
s->link.data = s;
s->link.next = NULL;
s->link.prev = NULL;
g_hash_table_add (data->strings, s);
g_queue_push_tail_link (&data->string_list, &s->link);
return s;
}
static void
record_start_element (GMarkupParseContext *context,
const char *element_name,
@@ -131,21 +259,20 @@ record_start_element (GMarkupParseContext *context,
{
gsize n_attrs = g_strv_length ((char **)names);
RecordData *data = user_data;
RecordDataTree *child;
RecordDataElement *child;
RecordDataString *name, **attr_names, **attr_values;
int i;
child = record_data_tree_new (data->current, RECORD_TYPE_ELEMENT,
record_data_string_lookup (data->strings, element_name, -1));
name = record_data_string_lookup (data, element_name, -1);
child = record_data_element_new (data->current, name, n_attrs);
data->current = child;
child->n_attributes = n_attrs;
child->attributes = g_new (const char *, n_attrs);
child->values = g_new (const char *, n_attrs);
attr_names = &child->attributes[0];
attr_values = &child->attributes[n_attrs];
for (i = 0; i < n_attrs; i++)
{
child->attributes[i] = record_data_string_lookup (data->strings, names[i], -1);
child->values[i] = record_data_string_lookup (data->strings, values[i], -1);
attr_names[i] = record_data_string_lookup (data, names[i], -1);
attr_values[i] = record_data_string_lookup (data, values[i], -1);
}
}
@@ -160,6 +287,23 @@ record_end_element (GMarkupParseContext *context,
data->current = data->current->parent;
}
static gboolean
is_whitespace (const char *text,
gsize text_len)
{
const char *end;
const char *p;
end = text + text_len;
for (p = text; p < end; p = g_utf8_next_char (p))
{
if (!g_unichar_isspace (g_utf8_get_char (p)))
return FALSE;
}
return TRUE;
}
static void
record_text (GMarkupParseContext *context,
const char *text,
@@ -168,9 +312,16 @@ record_text (GMarkupParseContext *context,
GError **error)
{
RecordData *data = user_data;
RecordDataString *string;
record_data_tree_new (data->current, RECORD_TYPE_TEXT,
record_data_string_lookup (data->strings, text, text_len));
if (text_len == 0)
return;
if (!data->current->preserve_whitespace && is_whitespace (text, text_len))
return;
string = record_data_string_lookup (data, text, text_len);
record_data_element_append_text (data->current, string);
}
static const GMarkupParser record_parser =
@@ -182,16 +333,6 @@ static const GMarkupParser record_parser =
NULL, // error, fails immediately
};
static int
compare_string (gconstpointer _a,
gconstpointer _b)
{
const RecordDataString *a = _a;
const RecordDataString *b = _b;
return b->count - a->count;
}
static void
marshal_uint32 (GString *str,
guint32 v)
@@ -240,54 +381,60 @@ marshal_uint32 (GString *str,
}
}
static void
marshal_string (GString *marshaled,
GHashTable *strings,
const char *string)
static int
marshal_uint32_len (guint32 v)
{
RecordDataString *s;
if (v < 128)
return 1;
s = g_hash_table_lookup (strings, string);
g_assert (s != NULL);
if (v < (1<<14))
return 2;
marshal_uint32 (marshaled, s->offset);
if (v < (1<<21))
return 3;
if (v < (1<<28))
return 4;
return 5;
}
static void
marshal_tree (GString *marshaled,
GHashTable *strings,
RecordDataTree *tree)
RecordDataNode *node)
{
GList *l;
int i;
RecordDataText *text;
RecordDataElement *element;
RecordDataString **attr_names, **attr_values;
/* Special case the root */
if (tree->parent == NULL)
{
for (l = g_list_last (tree->children); l != NULL; l = l->prev)
marshal_tree (marshaled, strings, l->data);
return;
}
switch (tree->type)
switch (node->type)
{
case RECORD_TYPE_ELEMENT:
element = (RecordDataElement *)node;
marshal_uint32 (marshaled, RECORD_TYPE_ELEMENT);
marshal_string (marshaled, strings, tree->data);
marshal_uint32 (marshaled, tree->n_attributes);
for (i = 0; i < tree->n_attributes; i++)
marshal_uint32 (marshaled, element->name->offset);
marshal_uint32 (marshaled, element->n_attributes);
attr_names = &element->attributes[0];
attr_values = &element->attributes[element->n_attributes];
for (i = 0; i < element->n_attributes; i++)
{
marshal_string (marshaled, strings, tree->attributes[i]);
marshal_string (marshaled, strings, tree->values[i]);
marshal_uint32 (marshaled, attr_names[i]->offset);
marshal_uint32 (marshaled, attr_values[i]->offset);
}
for (l = g_list_last (tree->children); l != NULL; l = l->prev)
marshal_tree (marshaled, strings, l->data);
for (l = element->children.head; l != NULL; l = l->next)
marshal_tree (marshaled, l->data);
marshal_uint32 (marshaled, RECORD_TYPE_END_ELEMENT);
break;
case RECORD_TYPE_TEXT:
text = (RecordDataText *)node;
marshal_uint32 (marshaled, RECORD_TYPE_TEXT);
marshal_string (marshaled, strings, tree->data);
marshal_uint32 (marshaled, text->string->text_offset);
break;
case RECORD_TYPE_END_ELEMENT:
default:
@@ -295,6 +442,17 @@ marshal_tree (GString *marshaled,
}
}
static void
marshal_root (GString *marshaled,
RecordDataNode *node)
{
GList *l;
RecordDataElement *element = (RecordDataElement *)node;
for (l = element->children.head; l != NULL; l = l->next)
marshal_tree (marshaled, l->data);
}
/**
* _gtk_buildable_parser_precompile:
* @text: chunk of text to parse
@@ -313,20 +471,22 @@ _gtk_buildable_parser_precompile (const char *text,
{
GMarkupParseContext *ctx;
RecordData data = { 0 };
GList *string_table, *l;
GList *l;
GString *marshaled;
int offset;
data.strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)record_data_string_free);
data.root = record_data_tree_new (NULL, RECORD_TYPE_ELEMENT, NULL);
data.strings = g_hash_table_new (record_data_string_hash, record_data_string_equal);
data.chunks = g_string_chunk_new (512);
data.root = record_data_element_new (NULL, NULL, 0);
data.current = data.root;
ctx = g_markup_parse_context_new (&record_parser, G_MARKUP_TREAT_CDATA_AS_TEXT,
&data, NULL);
ctx = g_markup_parse_context_new (&record_parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
if (!g_markup_parse_context_parse (ctx, text, text_len, error))
if (!g_markup_parse_context_parse (ctx, text, text_len, error) ||
!g_markup_parse_context_end_parse (ctx, error))
{
record_data_tree_free (data.root);
record_data_node_free (&data.root->base);
g_string_chunk_free (data.chunks);
g_hash_table_destroy (data.strings);
g_markup_parse_context_free (ctx);
return NULL;
@@ -334,34 +494,45 @@ _gtk_buildable_parser_precompile (const char *text,
g_markup_parse_context_free (ctx);
string_table = g_hash_table_get_values (data.strings);
string_table = g_list_sort (string_table, compare_string);
g_queue_sort (&data.string_list, record_data_string_compare, NULL);
offset = 0;
for (l = string_table; l != NULL; l = l->next)
for (l = data.string_list.head; l != NULL; l = l->next)
{
RecordDataString *s = l->data;
if (s->include_len)
{
s->text_offset = offset;
offset += marshal_uint32_len (s->len);
}
s->offset = offset;
offset += strlen (s->string) + 1;
offset += s->len + 1;
}
marshaled = g_string_new ("");
marshaled = g_string_sized_new (4 + offset + 32);
/* Magic marker */
g_string_append_len (marshaled, "GBU\0", 4);
marshal_uint32 (marshaled, offset);
for (l = string_table; l != NULL; l = l->next)
for (l = data.string_list.head; l != NULL; l = l->next)
{
RecordDataString *s = l->data;
g_string_append_len (marshaled, s->string, strlen (s->string) + 1);
if (s->include_len)
marshal_uint32 (marshaled, s->len);
g_string_append_len (marshaled, s->string, s->len + 1);
}
g_list_free (string_table);
marshal_root (marshaled, &data.root->base);
marshal_tree (marshaled, data.strings, data.root);
record_data_tree_free (data.root);
g_slice_free_chain (RecordDataString,
(RecordDataString *)data.string_list.head,
link.next);
record_data_node_free (&data.root->base);
g_string_chunk_free (data.chunks);
g_hash_table_destroy (data.strings);
return g_string_free_to_bytes (marshaled);
@@ -412,6 +583,18 @@ demarshal_string (const char **tree,
return strings + offset;
}
static const char *
demarshal_text (const char **tree,
const char *strings,
guint32 *len)
{
guint32 offset = demarshal_uint32 (tree);
const char *str = strings + offset;
*len = demarshal_uint32 (&str);
return str;
}
static void
propagate_error (GtkBuildableParseContext *context,
GError **dest,
@@ -489,14 +672,15 @@ replay_text (GtkBuildableParseContext *context,
const char *strings,
GError **error)
{
guint32 len;
const char *text;
GError *tmp_error = NULL;
text = demarshal_string (tree, strings);
text = demarshal_text (tree, strings, &len);
(*context->internal_callbacks->text) (NULL,
text,
strlen (text),
len,
context,
&tmp_error);
+1
View File
@@ -296,6 +296,7 @@ gtk_color_button_init (GtkColorButton *button)
"accessible-role", GTK_ACCESSIBLE_ROLE_IMG,
"selectable", FALSE,
"has-menu", FALSE,
"can-drag", FALSE,
NULL);
gtk_widget_set_can_focus (button->swatch, FALSE);
gtk_widget_remove_css_class (button->swatch, "activatable");
+42 -13
View File
@@ -65,6 +65,7 @@ struct _GtkColorSwatch
GtkWidget *popover;
GtkDropTarget *dest;
GtkDragSource *source;
};
struct _GtkColorSwatchClass
@@ -81,7 +82,8 @@ enum
PROP_RGBA,
PROP_SELECTABLE,
PROP_HAS_MENU,
PROP_CAN_DROP
PROP_CAN_DROP,
PROP_CAN_DRAG
};
G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
@@ -429,6 +431,9 @@ swatch_get_property (GObject *object,
case PROP_CAN_DROP:
g_value_set_boolean (value, swatch->dest != NULL);
break;
case PROP_CAN_DRAG:
g_value_set_boolean (value, swatch->source != NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -457,6 +462,9 @@ swatch_set_property (GObject *object,
case PROP_CAN_DROP:
gtk_color_swatch_set_can_drop (swatch, g_value_get_boolean (value));
break;
case PROP_CAN_DRAG:
gtk_color_swatch_set_can_drag (swatch, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -470,7 +478,7 @@ swatch_finalize (GObject *object)
g_free (swatch->icon);
gtk_widget_unparent (swatch->overlay_widget);
G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
}
@@ -512,11 +520,14 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class)
g_object_class_install_property (object_class, PROP_CAN_DROP,
g_param_spec_boolean ("can-drop", P_("Can Drop"), P_("Whether the swatch should accept drops"),
FALSE, GTK_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_CAN_DRAG,
g_param_spec_boolean ("can-drag", P_("Can Drag"), P_("Whether the swatch should allow drags"),
TRUE, GTK_PARAM_READWRITE));
/**
* GtkColorSwatch|menu.popup:
*
* Opens the context menu.
* Opens the context menu.
*/
gtk_widget_class_install_action (widget_class, "menu.popup", NULL, swatch_popup_menu);
@@ -568,6 +579,8 @@ gtk_color_swatch_init (GtkColorSwatch *swatch)
G_CALLBACK (key_controller_key_pressed), swatch);
gtk_widget_add_controller (GTK_WIDGET (swatch), controller);
gtk_color_swatch_set_can_drag (swatch, TRUE);
gtk_widget_add_css_class (GTK_WIDGET (swatch), "activatable");
swatch->overlay_widget = g_object_new (GTK_TYPE_IMAGE,
@@ -598,18 +611,10 @@ void
gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
const GdkRGBA *color)
{
if (!swatch->has_color)
{
GtkDragSource *source;
source = gtk_drag_source_new ();
g_signal_connect (source, "prepare", G_CALLBACK (gtk_color_swatch_drag_prepare), swatch);
gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (source));
}
swatch->has_color = TRUE;
swatch->color = *color;
if (swatch->source)
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (swatch->source), GTK_PHASE_CAPTURE);
if (INTENSITY (swatch->color.red, swatch->color.green, swatch->color.blue) > 0.5)
{
@@ -681,6 +686,30 @@ gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
g_object_notify (G_OBJECT (swatch), "can-drop");
}
void
gtk_color_swatch_set_can_drag (GtkColorSwatch *swatch,
gboolean can_drag)
{
if (can_drag == (swatch->source != NULL))
return;
if (can_drag && !swatch->source)
{
swatch->source = gtk_drag_source_new ();
g_signal_connect (swatch->source, "prepare", G_CALLBACK (gtk_color_swatch_drag_prepare), swatch);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (swatch->source),
swatch->has_color ? GTK_PHASE_CAPTURE : GTK_PHASE_NONE);
gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (swatch->source));
}
if (!can_drag && swatch->source)
{
gtk_widget_remove_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (swatch->source));
swatch->source = NULL;
}
g_object_notify (G_OBJECT (swatch), "can-drag");
}
void
gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
gboolean use_alpha)
+2
View File
@@ -42,6 +42,8 @@ void gtk_color_swatch_set_hsva (GtkColorSwatch *swatch,
double a);
void gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
gboolean can_drop);
void gtk_color_swatch_set_can_drag (GtkColorSwatch *swatch,
gboolean can_drag);
void gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
const char *icon);
void gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
+25 -1
View File
@@ -386,6 +386,7 @@ gtk_drag_icon_class_init (GtkDragIconClass *klass)
static void
gtk_drag_icon_init (GtkDragIcon *self)
{
gtk_widget_set_can_target (GTK_WIDGET (self), FALSE);
}
/**
@@ -531,15 +532,38 @@ gtk_drag_icon_create_widget_for_value (const GValue *value)
{
return gtk_label_new (g_value_get_string (value));
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE))
{
GtkWidget *image;
image = gtk_image_new_from_paintable (g_value_get_object (value));
gtk_widget_add_css_class (image, "large-icons");
return image;
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA))
{
GtkWidget *swatch;
swatch = gtk_color_swatch_new ();
gtk_color_swatch_set_can_drag (GTK_COLOR_SWATCH (swatch), FALSE);
gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (swatch), FALSE);
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (swatch), g_value_get_boxed (value));
return swatch;
}
else if (G_VALUE_HOLDS (value, G_TYPE_FILE))
{
GFileInfo *info;
GtkWidget *image;
info = g_file_query_info (G_FILE (g_value_get_object (value)), "standard::icon", 0, NULL, NULL);
image = gtk_image_new_from_gicon (g_file_info_get_icon (info));
gtk_widget_add_css_class (image, "large-icons");
g_object_unref (info);
return image;
}
else if (G_VALUE_HOLDS (value, GTK_TYPE_TEXT_BUFFER))
{
GtkTextBuffer *buffer = g_value_get_object (value);
@@ -568,7 +592,7 @@ gtk_drag_icon_create_widget_for_value (const GValue *value)
node = gsk_value_get_render_node (value);
if (node == NULL)
return NULL;
gsk_render_node_get_bounds (node, &bounds);
paintable = gtk_render_node_paintable_new (node, &bounds);
image = gtk_image_new_from_paintable (paintable);
+9 -8
View File
@@ -80,6 +80,7 @@
* GDK_TYPE_PIXBUF,
* }, 2);
*
* g_signal_connect (target, "drop", G_CALLBACK (on_drop), self);
* gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
* }
* ```
@@ -303,7 +304,7 @@ gtk_drop_target_load (GtkDropTarget *self)
self->cancellable = g_cancellable_new ();
gdk_drop_read_value_async (self->drop,
gdk_drop_read_value_async (self->drop,
type,
G_PRIORITY_DEFAULT,
self->cancellable,
@@ -355,13 +356,13 @@ make_action_unique (GdkDragAction actions)
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_LINK)
return GDK_ACTION_LINK;
return 0;
}
static GdkDragAction
gtk_drop_target_enter (GtkDropTarget *self,
double x,
@@ -370,7 +371,7 @@ gtk_drop_target_enter (GtkDropTarget *self,
return make_action_unique (self->actions & gdk_drop_get_actions (self->drop));
}
static GdkDragAction
static GdkDragAction
gtk_drop_target_motion (GtkDropTarget *self,
double x,
double y)
@@ -900,7 +901,7 @@ GdkContentFormats *
gtk_drop_target_get_formats (GtkDropTarget *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
return self->formats;
}
@@ -929,7 +930,7 @@ gtk_drop_target_set_gtypes (GtkDropTarget *self,
builder = gdk_content_formats_builder_new ();
for (i = 0; i < n_types; i++)
gdk_content_formats_builder_add_gtype (builder, types[i]);
self->formats = gdk_content_formats_builder_free_to_formats (builder);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
@@ -970,7 +971,7 @@ gtk_drop_target_set_actions (GtkDropTarget *self,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DROP_TARGET (self));
if (self->actions == actions)
return;
@@ -1007,7 +1008,7 @@ gtk_drop_target_set_preload (GtkDropTarget *self,
gboolean preload)
{
g_return_if_fail (GTK_IS_DROP_TARGET (self));
if (self->preload == preload)
return;
+1 -1
View File
@@ -124,7 +124,7 @@
*
* `GtkBuilder` has support for creating expressions. The syntax here can be used where
* a `GtkExpression` object is needed like in a `<property>` tag for an expression
* property, or in a `<binding>` tag to bind a property to an expression.
* property, or in a `<binding name="property">` tag to bind a property to an expression.
*
* To create an property expression, use the `<lookup>` element. It can have a `type`
* attribute to specify the object type, and a `name` attribute to specify the property
+6 -1
View File
@@ -305,7 +305,7 @@ static void
quartz_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
{
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
GtkWidget* surface_widget = GTK_WIDGET (gdk_surface_get_widget (qc->client_surface));
GtkWidget* surface_widget;
int sx, sy;
double wx, wy;
@@ -317,6 +317,11 @@ quartz_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
if (!qc->focused)
return;
surface_widget = GTK_WIDGET (gdk_surface_get_widget (qc->client_surface));
if (!surface_widget)
return;
gdk_surface_get_origin (qc->client_surface, &sx, &sy);
gtk_widget_translate_coordinates(qc->client_widget, surface_widget,
area->x, area->y, &wx, &wy);
+25 -6
View File
@@ -286,7 +286,7 @@ gtk_menu_tracker_item_update_visibility (GtkMenuTrackerItem *self)
if (visible != self->is_visible)
{
self->is_visible = visible;
g_object_notify (G_OBJECT (self), "is-visible");
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_IS_VISIBLE]);
}
}
@@ -300,9 +300,17 @@ gtk_menu_tracker_item_action_added (GtkActionObserver *observer,
{
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
GVariant *action_target;
gboolean old_sensitive;
gboolean old_toggled;
GtkMenuTrackerItemRole old_role;
guint n_changed;
GTK_NOTE(ACTIONS, g_message ("menutracker: action %s added", action_name));
old_sensitive = self->sensitive;
old_toggled = self->toggled;
old_role = self->role;
action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL);
self->can_activate = (action_target == NULL && parameter_type == NULL) ||
@@ -340,18 +348,29 @@ gtk_menu_tracker_item_action_added (GtkActionObserver *observer,
self->role = GTK_MENU_TRACKER_ITEM_ROLE_CHECK;
}
g_object_freeze_notify (G_OBJECT (self));
/* Avoid freeze/thaw_notify as they are quite expensive in runtime/memory
* unless we have more than one property to update. Additionally, only
* notify on properties that have changed to avoid extraneous signal
* emission. This code can get run a lot!
*/
n_changed = (old_role != self->role)
+ (old_toggled != self->toggled)
+ (old_sensitive != self->sensitive);
if (self->sensitive)
if (n_changed > 1)
g_object_freeze_notify (G_OBJECT (self));
if (self->sensitive != old_sensitive)
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
if (self->toggled)
if (self->toggled != old_toggled)
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]);
if (self->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
if (self->role != old_role)
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ROLE]);
g_object_thaw_notify (G_OBJECT (self));
if (n_changed > 1)
g_object_thaw_notify (G_OBJECT (self));
if (action_target)
g_variant_unref (action_target);
+2 -1
View File
@@ -178,7 +178,8 @@ gtk_picture_measure (GtkWidget *widget,
double min_width, min_height, nat_width, nat_height;
double default_size;
if (self->paintable == NULL)
/* for_size = 0 below is treated as -1, but we want to return zeros. */
if (self->paintable == NULL || for_size == 0)
{
*minimum = 0;
*natural = 0;
+21 -40
View File
@@ -1289,28 +1289,6 @@ get_border (GtkCssNode *node,
border->left = _gtk_css_number_value_get (style->border->border_left_width, 100);
}
static GskPath *
gtk_popover_get_tail_path (GtkPopover *popover)
{
GskPathBuilder *builder;
int initial_x, initial_y;
int tip_x, tip_y;
int final_x, final_y;
builder = gsk_path_builder_new ();
gtk_popover_get_gap_coords (popover,
&initial_x, &initial_y,
&tip_x, &tip_y,
&final_x, &final_y);
gsk_path_builder_move_to (builder, initial_x, initial_y);
gsk_path_builder_line_to (builder, tip_x, tip_y);
gsk_path_builder_line_to (builder, final_x, final_y);
return gsk_path_builder_free_to_path (builder);
}
static void
gtk_popover_apply_tail_path (GtkPopover *popover,
cairo_t *cr)
@@ -1565,14 +1543,22 @@ create_arrow_render_node (GtkPopover *popover)
GtkWidget *widget = GTK_WIDGET (popover);
GtkStyleContext *context;
GtkBorder border;
cairo_t *cr;
GtkSnapshot *snapshot;
GskPath *path;
snapshot = gtk_snapshot_new ();
cr = gtk_snapshot_append_cairo (snapshot,
&GRAPHENE_RECT_INIT (
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)
));
/* Clip to the arrow shape */
path = gtk_popover_get_tail_path (popover);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
cairo_save (cr);
gtk_popover_apply_tail_path (popover, cr);
cairo_clip (cr);
get_border (priv->arrow_node, &border);
@@ -1580,34 +1566,29 @@ create_arrow_render_node (GtkPopover *popover)
gtk_style_context_save_to_node (context, priv->arrow_node);
/* Render the arrow background */
gtk_snapshot_render_background (snapshot, context,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
gtk_render_background (context, cr,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
/* Render the border of the arrow tip */
if (border.bottom > 0)
{
GtkCssStyle *style;
const GdkRGBA *border_color;
GskStroke *stroke;
graphene_rect_t bounds;
style = gtk_css_node_get_style (priv->arrow_node);
border_color = gtk_css_color_value_get_rgba (style->border->border_left_color ? style->border->border_left_color : style->core->color);
stroke = gsk_stroke_new (border.bottom + 1);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_popover_apply_tail_path (popover, cr);
gdk_cairo_set_source_rgba (cr, border_color);
gsk_path_get_bounds (path, &bounds);
gtk_snapshot_append_color (snapshot, border_color, &bounds);
gtk_snapshot_pop (snapshot);
cairo_set_line_width (cr, border.bottom + 1);
cairo_stroke (cr);
}
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
cairo_restore (cr);
cairo_destroy (cr);
gtk_style_context_restore (context);
+93 -93
View File
@@ -1,5 +1,5 @@
/* GTK - The GIMP Toolkit
* gtkprintoperation-unix.c: Print Operation Details for Unix
* gtkprintoperation-unix.c: Print Operation Details for Unix
* and Unix-like platforms
* Copyright (C) 2006, Red Hat, Inc.
*
@@ -25,7 +25,7 @@
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdlib.h>
#include <fcntl.h>
#include <glib/gstdio.h>
@@ -45,7 +45,7 @@
#include "gtkintl.h"
typedef struct
typedef struct
{
GtkWindow *parent; /* just in case we need to throw error dialogs */
GMainLoop *loop;
@@ -56,7 +56,7 @@ typedef struct
cairo_surface_t *surface;
gulong job_status_changed_tag;
} GtkPrintOperationUnix;
typedef struct _PrinterFinder PrinterFinder;
@@ -71,18 +71,18 @@ unix_start_page (GtkPrintOperation *op,
GtkPrintContext *print_context,
GtkPageSetup *page_setup)
{
GtkPrintOperationUnix *op_unix;
GtkPrintOperationUnix *op_unix;
GtkPaperSize *paper_size;
cairo_surface_type_t type;
double w, h;
op_unix = op->priv->platform_data;
paper_size = gtk_page_setup_get_paper_size (page_setup);
w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
type = cairo_surface_get_type (op_unix->surface);
if ((op->priv->manual_number_up < 2) ||
@@ -167,11 +167,11 @@ shell_command_substitute_file (const char *cmd,
*settings_filename_replaced = FALSE;
start = inptr = cmd;
while ((inptr = strchr (inptr, '%')) != NULL)
while ((inptr = strchr (inptr, '%')) != NULL)
{
g_string_append_len (final, start, inptr - start);
inptr++;
switch (*inptr)
switch (*inptr)
{
case 'f':
g_string_append (final, pdf_filename);
@@ -236,11 +236,11 @@ gtk_print_operation_unix_launch_preview (GtkPrintOperation *op,
display = gdk_display_get_default ();
fd = g_file_open_tmp ("settingsXXXXXX.ini", &settings_filename, &error);
if (fd < 0)
if (fd < 0)
goto out;
key_file = g_key_file_new ();
print_settings = gtk_print_settings_copy (gtk_print_operation_get_print_settings (op));
if (print_settings != NULL)
@@ -345,7 +345,7 @@ gtk_print_operation_unix_launch_preview (GtkPrintOperation *op,
static void
unix_finish_send (GtkPrintJob *job,
gpointer user_data,
gpointer user_data,
const GError *error)
{
GtkPrintOperation *op = (GtkPrintOperation *) user_data;
@@ -370,19 +370,19 @@ unix_end_run (GtkPrintOperation *op,
GtkPrintOperationUnix *op_unix = op->priv->platform_data;
cairo_surface_finish (op_unix->surface);
if (cancelled)
return;
if (wait)
op_unix->loop = g_main_loop_new (NULL, FALSE);
/* TODO: Check for error */
if (op_unix->job != NULL)
{
g_object_ref (op);
gtk_print_job_send (op_unix->job,
unix_finish_send,
unix_finish_send,
op, NULL);
}
@@ -398,7 +398,7 @@ unix_end_run (GtkPrintOperation *op,
}
static void
job_status_changed_cb (GtkPrintJob *job,
job_status_changed_cb (GtkPrintJob *job,
GtkPrintOperation *op)
{
_gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
@@ -406,7 +406,7 @@ job_status_changed_cb (GtkPrintJob *job,
static void
print_setup_changed_cb (GtkPrintUnixDialog *print_dialog,
print_setup_changed_cb (GtkPrintUnixDialog *print_dialog,
GParamSpec *pspec,
gpointer user_data)
{
@@ -450,13 +450,13 @@ get_print_dialog (GtkPrintOperation *op,
priv->print_settings);
if (priv->default_page_setup)
gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
priv->default_page_setup);
gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (pd),
priv->embed_page_setup);
gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (pd),
gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (pd),
priv->current_page);
gtk_print_unix_dialog_set_support_selection (GTK_PRINT_UNIX_DIALOG (pd),
@@ -468,10 +468,10 @@ get_print_dialog (GtkPrintOperation *op,
g_signal_emit_by_name (op, "create-custom-widget",
&priv->custom_widget);
if (priv->custom_widget)
if (priv->custom_widget)
{
custom_tab_label = priv->custom_tab_label;
if (custom_tab_label == NULL)
{
custom_tab_label = g_get_application_name ();
@@ -480,18 +480,18 @@ get_print_dialog (GtkPrintOperation *op,
}
label = gtk_label_new (custom_tab_label);
gtk_print_unix_dialog_add_custom_tab (GTK_PRINT_UNIX_DIALOG (pd),
priv->custom_widget, label);
g_signal_connect (pd, "notify::selected-printer", (GCallback) print_setup_changed_cb, op);
g_signal_connect (pd, "notify::page-setup", (GCallback) print_setup_changed_cb, op);
}
return pd;
}
typedef struct
typedef struct
{
GtkPrintOperation *op;
gboolean do_print;
@@ -523,7 +523,7 @@ finish_print (PrintResponseData *rdata,
GtkPrintOperationPrivate *priv = op->priv;
GtkPrintJob *job;
double top, bottom, left, right;
if (rdata->do_print)
{
gtk_print_operation_set_print_settings (op, settings);
@@ -555,38 +555,38 @@ finish_print (PrintResponseData *rdata,
{
GtkPrintOperationUnix *op_unix;
cairo_t *cr;
op_unix = g_new0 (GtkPrintOperationUnix, 1);
priv->platform_data = op_unix;
priv->free_platform_data = (GDestroyNotify) op_unix_free;
op_unix->parent = rdata->parent;
priv->start_page = unix_start_page;
priv->end_page = unix_end_page;
priv->end_run = unix_end_run;
job = gtk_print_job_new (priv->job_name, printer, settings, page_setup);
op_unix->job = job;
gtk_print_job_set_track_print_status (job, priv->track_print_status);
op_unix->surface = gtk_print_job_get_surface (job, &priv->error);
if (op_unix->surface == NULL)
if (op_unix->surface == NULL)
{
rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR;
rdata->do_print = FALSE;
goto out;
}
cr = cairo_create (op_unix->surface);
gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
cairo_destroy (cr);
_gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL);
op_unix->job_status_changed_tag =
g_signal_connect (job, "status-changed",
G_CALLBACK (job_status_changed_cb), op);
priv->print_pages = gtk_print_job_get_pages (job);
priv->page_ranges = gtk_print_job_get_page_ranges (job, &priv->num_page_ranges);
priv->manual_num_copies = gtk_print_job_get_num_copies (job);
@@ -598,16 +598,16 @@ finish_print (PrintResponseData *rdata,
priv->manual_number_up = gtk_print_job_get_n_up (job);
priv->manual_number_up_layout = gtk_print_job_get_n_up_layout (job);
}
}
}
out:
if (rdata->print_cb)
rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result);
rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result);
if (rdata->destroy)
rdata->destroy (rdata);
}
static void
static void
handle_print_response (GtkWidget *dialog,
int response,
gpointer data)
@@ -627,7 +627,7 @@ handle_print_response (GtkWidget *dialog,
rdata->do_preview = FALSE;
if (printer != NULL)
rdata->do_print = TRUE;
}
}
else if (response == GTK_RESPONSE_APPLY)
{
/* print preview */
@@ -650,15 +650,15 @@ handle_print_response (GtkWidget *dialog,
gtk_print_operation_set_print_settings (rdata->op, settings);
g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget);
}
if (rdata->loop)
g_main_loop_quit (rdata->loop);
finish_print (rdata, printer, page_setup, settings, page_setup_set);
if (settings)
g_object_unref (settings);
gtk_window_destroy (GTK_WINDOW (pd));
}
@@ -675,7 +675,7 @@ found_printer (GtkPrinter *printer,
if (rdata->loop)
g_main_loop_quit (rdata->loop);
if (printer != NULL)
if (printer != NULL)
{
rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY;
@@ -688,18 +688,18 @@ found_printer (GtkPrinter *printer,
gtk_print_settings_set_printer (settings,
gtk_printer_get_name (printer));
if (priv->default_page_setup)
page_setup = gtk_page_setup_copy (priv->default_page_setup);
else
page_setup = gtk_page_setup_new ();
}
finish_print (rdata, printer, page_setup, settings, FALSE);
if (settings)
g_object_unref (settings);
if (page_setup)
g_object_unref (page_setup);
}
@@ -723,13 +723,13 @@ gtk_print_operation_unix_run_dialog_async (GtkPrintOperation *op,
rdata->parent = parent;
rdata->loop = NULL;
rdata->destroy = print_response_data_free;
if (show_dialog)
{
pd = get_print_dialog (op, parent);
gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
g_signal_connect (pd, "response",
g_signal_connect (pd, "response",
G_CALLBACK (handle_print_response), rdata);
gtk_window_present (GTK_WINDOW (pd));
@@ -739,7 +739,7 @@ gtk_print_operation_unix_run_dialog_async (GtkPrintOperation *op,
printer_name = NULL;
if (op->priv->print_settings)
printer_name = gtk_print_settings_get_printer (op->priv->print_settings);
find_printer (printer_name, (GFunc) found_printer, rdata);
}
}
@@ -751,8 +751,8 @@ write_preview (void *closure,
{
int fd = GPOINTER_TO_INT (closure);
gssize written;
while (length > 0)
while (length > 0)
{
written = write (fd, data, length);
@@ -760,9 +760,9 @@ write_preview (void *closure,
{
if (errno == EAGAIN || errno == EINTR)
continue;
return CAIRO_STATUS_WRITE_ERROR;
}
}
data += written;
length -= written;
@@ -792,7 +792,7 @@ gtk_print_operation_unix_create_preview_surface (GtkPrintOperation *op,
double w, h;
cairo_surface_t *surface;
static cairo_user_data_key_t key;
filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL);
fd = g_mkstemp (filename);
@@ -803,14 +803,14 @@ gtk_print_operation_unix_create_preview_surface (GtkPrintOperation *op,
}
*target = filename;
paper_size = gtk_page_setup_get_paper_size (page_setup);
w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
*dpi_x = *dpi_y = 72;
surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER (fd), w, h);
cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview);
return surface;
@@ -837,7 +837,7 @@ gtk_print_operation_unix_resize_preview_surface (GtkPrintOperation *op,
cairo_surface_t *surface)
{
double w, h;
w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
cairo_pdf_surface_set_size (surface, w, h);
@@ -852,7 +852,7 @@ gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
GtkWidget *pd;
PrintResponseData rdata;
const char *printer_name;
rdata.op = op;
rdata.do_print = FALSE;
rdata.do_preview = FALSE;
@@ -867,7 +867,7 @@ gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
pd = get_print_dialog (op, parent);
gtk_window_set_modal (GTK_WINDOW (pd), TRUE);
g_signal_connect (pd, "response",
g_signal_connect (pd, "response",
G_CALLBACK (handle_print_response), &rdata);
gtk_window_present (GTK_WINDOW (pd));
@@ -882,7 +882,7 @@ gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
printer_name = NULL;
if (op->priv->print_settings)
printer_name = gtk_print_settings_get_printer (op->priv->print_settings);
rdata.loop = g_main_loop_new (NULL, FALSE);
find_printer (printer_name,
(GFunc) found_printer, &rdata);
@@ -893,12 +893,12 @@ gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
}
*do_print = rdata.do_print;
return rdata.result;
}
typedef struct
typedef struct
{
GtkPageSetup *page_setup;
GtkPageSetupDoneFunc done_cb;
@@ -967,13 +967,13 @@ get_page_setup_dialog (GtkWindow *parent,
*
* Runs a page setup dialog, letting the user modify the values from
* @page_setup. If the user cancels the dialog, the returned `GtkPageSetup`
* is identical to the passed in @page_setup, otherwise it contains the
* is identical to the passed in @page_setup, otherwise it contains the
* modifications done in the dialog.
*
* Note that this function may use a recursive mainloop to show the page
* setup dialog. See gtk_print_run_page_setup_dialog_async() if this is
* setup dialog. See gtk_print_run_page_setup_dialog_async() if this is
* a problem.
*
*
* Returns: (transfer full): a new `GtkPageSetup`
*/
GtkPageSetup *
@@ -982,8 +982,8 @@ gtk_print_run_page_setup_dialog (GtkWindow *parent,
GtkPrintSettings *settings)
{
GtkWidget *dialog;
PageSetupResponseData rdata;
PageSetupResponseData rdata;
rdata.page_setup = NULL;
rdata.done_cb = NULL;
rdata.data = NULL;
@@ -997,7 +997,7 @@ gtk_print_run_page_setup_dialog (GtkWindow *parent,
&rdata);
gtk_window_present (GTK_WINDOW (dialog));
g_main_loop_run (rdata.loop);
g_main_loop_unref (rdata.loop);
rdata.loop = NULL;
@@ -1034,10 +1034,10 @@ gtk_print_run_page_setup_dialog_async (GtkWindow *parent,
{
GtkWidget *dialog;
PageSetupResponseData *rdata;
dialog = get_page_setup_dialog (parent, page_setup, settings);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
rdata = g_new (PageSetupResponseData, 1);
rdata->page_setup = NULL;
rdata->done_cb = done_cb;
@@ -1051,7 +1051,7 @@ gtk_print_run_page_setup_dialog_async (GtkWindow *parent,
gtk_window_present (GTK_WINDOW (dialog));
}
struct _PrinterFinder
struct _PrinterFinder
{
gboolean found_printer;
GFunc func;
@@ -1087,8 +1087,8 @@ find_printer_idle (gpointer data)
}
static void
printer_added_cb (GtkPrintBackend *backend,
GtkPrinter *printer,
printer_added_cb (GtkPrintBackend *backend,
GtkPrinter *printer,
PrinterFinder *finder)
{
if (finder->found_printer)
@@ -1112,26 +1112,28 @@ printer_added_cb (GtkPrintBackend *backend,
finder->found_printer = TRUE;
}
else
if (finder->first_printer == NULL)
finder->first_printer = g_object_ref (printer);
{
if (finder->first_printer == NULL)
finder->first_printer = g_object_ref (printer);
}
if (finder->found_printer)
g_idle_add (find_printer_idle, finder);
}
static void
printer_list_done_cb (GtkPrintBackend *backend,
printer_list_done_cb (GtkPrintBackend *backend,
PrinterFinder *finder)
{
finder->backends = g_list_remove (finder->backends, backend);
g_signal_handlers_disconnect_by_func (backend, printer_added_cb, finder);
g_signal_handlers_disconnect_by_func (backend, printer_list_done_cb, finder);
gtk_print_backend_destroy (backend);
g_object_unref (backend);
if (finder->backends == NULL && !finder->found_printer)
if (finder->backends == NULL)
g_idle_add (find_printer_idle, finder);
}
@@ -1164,11 +1166,11 @@ find_printer_init (PrinterFinder *finder,
}
else
{
g_signal_connect (backend, "printer-added",
(GCallback) printer_added_cb,
g_signal_connect (backend, "printer-added",
(GCallback) printer_added_cb,
finder);
g_signal_connect (backend, "printer-list-done",
(GCallback) printer_list_done_cb,
g_signal_connect (backend, "printer-list-done",
(GCallback) printer_list_done_cb,
finder);
}
@@ -1178,15 +1180,15 @@ static void
printer_finder_free (PrinterFinder *finder)
{
GList *l;
g_free (finder->printer_name);
if (finder->printer)
g_object_unref (finder->printer);
if (finder->default_printer)
g_object_unref (finder->default_printer);
if (finder->first_printer)
g_object_unref (finder->first_printer);
@@ -1198,9 +1200,9 @@ printer_finder_free (PrinterFinder *finder)
gtk_print_backend_destroy (backend);
g_object_unref (backend);
}
g_list_free (finder->backends);
g_free (finder);
}
@@ -1228,10 +1230,8 @@ find_printer (const char *printer,
find_printer_init (finder, GTK_PRINT_BACKEND (node->data));
}
if (finder->backends == NULL && !finder->found_printer)
if (finder->backends == NULL)
g_idle_add (find_printer_idle, finder);
else
printer_finder_free (finder);
}
+74 -141
View File
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
}
static GskStroke *
create_stroke_style (double line_width,
GtkBorderStyle style,
double length)
static void
set_stroke_style (cairo_t *cr,
double line_width,
GtkBorderStyle style,
double length)
{
GskStroke *stroke;
float segments[2];
double segments[2];
double n;
stroke = gsk_stroke_new (line_width);
cairo_set_line_width (cr, line_width);
if (style == GTK_BORDER_STYLE_DOTTED)
{
@@ -374,12 +374,12 @@ create_stroke_style (double line_width,
segments[0] = 0;
segments[1] = n ? length / n : 2;
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
}
else if (style == GTK_BORDER_STYLE_DASHED)
else
{
n = length / line_width;
/* Optimize the common case of an integer-sized rectangle
@@ -397,33 +397,32 @@ create_stroke_style (double line_width,
segments[0] = n ? (1. / 3) * length / n : 1;
segments[1] = 2 * segments[0];
}
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
}
else
{
g_assert_not_reached ();
}
return stroke;
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *border_box,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
render_frame_stroke (cairo_t *cr,
const GskRoundedRect *border_box,
const double border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
gboolean different_colors, different_borders;
GskRoundedRect stroke_box;
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
guint i;
different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
!gdk_rgba_equal (&colors[0], &colors[2]) ||
!gdk_rgba_equal (&colors[0], &colors[3]);
different_borders = border_width[0] != border_width[1] ||
border_width[0] != border_width[2] ||
border_width[0] != border_width[3] ;
stroke_box = *border_box;
gsk_rounded_rect_shrink (&stroke_box,
border_width[GTK_CSS_TOP] / 2.0,
@@ -431,36 +430,32 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
border_width[GTK_CSS_BOTTOM] / 2.0,
border_width[GTK_CSS_LEFT] / 2.0);
if (border_width[0] == border_width[1] &&
border_width[0] == border_width[2] &&
border_width[0] == border_width[3] &&
hidden_side == 0)
if (!different_colors && !different_borders && hidden_side == 0)
{
double length = 0;
/* FAST PATH:
* Mostly expected to trigger for focus rectangles */
for (i = 0; i < 4; i++)
for (i = 0; i < 4; i++)
{
length += _gtk_rounded_box_guess_length (&stroke_box, i);
}
builder = gsk_path_builder_new ();
gsk_path_builder_add_rounded_rect (builder, &stroke_box);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[0],
stroke_style, length);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gtk_snapshot_pop (snapshot);
gsk_rounded_rect_path (&stroke_box, cr);
gdk_cairo_set_source_rgba (cr, &colors[0]);
set_stroke_style (cr, border_width[0], stroke_style, length);
cairo_stroke (cr);
}
else
{
const float weight = sqrtf(2)/2.0;
GskRoundedRect padding_box;
padding_box = *border_box;
gsk_rounded_rect_shrink (&padding_box,
border_width[GTK_CSS_TOP],
border_width[GTK_CSS_RIGHT],
border_width[GTK_CSS_BOTTOM],
border_width[GTK_CSS_LEFT]);
for (i = 0; i < 4; i++)
{
@@ -470,111 +465,49 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
if (border_width[i] == 0)
continue;
builder = gsk_path_builder_new ();
cairo_save (cr);
if (i == 0)
{
/* top */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width,
stroke_box.bounds.origin.y);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_top (border_box, &padding_box, cr);
else if (i == 1)
{
/* right */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_right (border_box, &padding_box, cr);
else if (i == 2)
{
/* bottom */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2,
weight);
}
_gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
else if (i == 3)
{
/* left */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
}
_gtk_rounded_box_path_left (border_box, &padding_box, cr);
cairo_clip (cr);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
_gtk_rounded_box_path_side (&stroke_box, cr, i);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gdk_cairo_set_source_rgba (cr, &colors[i]);
set_stroke_style (cr,
border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
cairo_stroke (cr);
gtk_snapshot_pop (snapshot);
cairo_restore (cr);
}
}
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *outline,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
cairo_t *cr;
cr = gtk_snapshot_append_cairo (snapshot,
&outline->bounds);
render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style);
cairo_destroy (cr);
}
static void
color_shade (const GdkRGBA *color,
double factor,
+3 -141
View File
@@ -34,7 +34,6 @@
#include "gsk/gskrendernodeprivate.h"
#include "gsk/gskroundedrectprivate.h"
#include "gsk/gskstrokeprivate.h"
#include "gtk/gskpango.h"
@@ -110,14 +109,6 @@ struct _GtkSnapshotState {
struct {
GskRoundedRect bounds;
} rounded_clip;
struct {
GskPath *path;
GskFillRule fill_rule;
} fill;
struct {
GskPath *path;
GskStroke stroke;
} stroke;
struct {
gsize n_shadows;
GskShadow *shadows;
@@ -770,7 +761,7 @@ gtk_snapshot_ensure_translate (GtkSnapshot *snapshot,
gtk_snapshot_autopush_transform (snapshot);
state = gtk_snapshot_get_current_state (snapshot);
}
gsk_transform_to_translate (state->transform, dx, dy);
}
@@ -859,7 +850,7 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot,
{
GtkSnapshotState *state;
float scale_x, scale_y, dx, dy;
gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
state = gtk_snapshot_push_state (snapshot,
@@ -1105,135 +1096,6 @@ gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
gsk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
}
static GskRenderNode *
gtk_snapshot_collect_fill (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *fill_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
fill_node = gsk_fill_node_new (node,
state->data.fill.path,
state->data.fill.fill_rule);
if (fill_node->bounds.size.width == 0 ||
fill_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (fill_node);
return NULL;
}
gsk_render_node_unref (node);
return fill_node;
}
static void
gtk_snapshot_clear_fill (GtkSnapshotState *state)
{
gsk_path_unref (state->data.fill.path);
}
/**
* gtk_snapshot_push_fill:
* @snapshot: a `GtkSnapshot`
* @path: The path describing the area to fill
* @fill_rule: The fill rule to use
*
* Fills the area given by @path and @fill_rule with an image and discards everything
* outside of it.
*
* The image is recorded until the next call to [method@Gtk.Snapshot.pop].
*/
void
gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_fill,
gtk_snapshot_clear_fill);
state->data.fill.path = gsk_path_ref (path);
state->data.fill.fill_rule = fill_rule;
}
static GskRenderNode *
gtk_snapshot_collect_stroke (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *stroke_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
stroke_node = gsk_stroke_node_new (node,
state->data.stroke.path,
&state->data.stroke.stroke);
if (stroke_node->bounds.size.width == 0 ||
stroke_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (stroke_node);
return NULL;
}
gsk_render_node_unref (node);
return stroke_node;
}
static void
gtk_snapshot_clear_stroke (GtkSnapshotState *state)
{
gsk_path_unref (state->data.stroke.path);
gsk_stroke_clear (&state->data.stroke.stroke);
}
/**
* gtk_snapshot_push_stroke:
* @snapshot: a `GtkSnapshot`
* @path: The path to stroke
* @stroke: The stroke attributes
*
* Strokes the given @path with the attributes given by @stroke and the
* image being recorded until the next call to [method@Gtk.Snapshot.pop].
*/
void
gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_stroke,
gtk_snapshot_clear_stroke);
state->data.stroke.path = gsk_path_ref (path);
gsk_stroke_init_copy (&state->data.stroke.stroke, stroke);
}
static GskRenderNode *
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
GtkSnapshotState *state,
@@ -2660,7 +2522,7 @@ gtk_snapshot_append_border (GtkSnapshot *snapshot,
gsk_rounded_rect_scale_affine (&real_outline, outline, scale_x, scale_y, dx, dy);
node = gsk_border_node_new (&real_outline,
(float[4]) {
(float[4]) {
border_width[0] * scale_y,
border_width[1] * scale_x,
border_width[2] * scale_y,
-8
View File
@@ -89,14 +89,6 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
const GskRoundedRect *bounds);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
const GskShadow *shadow,
gsize n_shadows);
+65 -65
View File
@@ -207,7 +207,7 @@ struct _GtkTextBTree {
int end_iter_segment_char_offset;
guint end_iter_line_stamp;
guint end_iter_segment_stamp;
GHashTable *child_anchor_table;
};
@@ -415,7 +415,7 @@ _gtk_text_btree_new (GtkTextTagTable *table,
tree->end_iter_line = NULL;
tree->end_iter_segment_byte_index = 0;
tree->end_iter_segment_char_offset = 0;
g_object_ref (tree->table);
tree->tag_changed_handler = g_signal_connect (tree->table,
@@ -425,7 +425,7 @@ _gtk_text_btree_new (GtkTextTagTable *table,
tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
tree->child_anchor_table = NULL;
/* We don't ref the buffer, since the buffer owns us;
* we'd have some circularity issues. The buffer always
* lasts longer than the BTree
@@ -489,20 +489,20 @@ _gtk_text_btree_unref (GtkTextBTree *tree)
tree->refcount -= 1;
if (tree->refcount == 0)
{
{
g_signal_handler_disconnect (tree->table,
tree->tag_changed_handler);
g_object_unref (tree->table);
tree->table = NULL;
gtk_text_btree_node_destroy (tree, tree->root_node);
tree->root_node = NULL;
g_assert (g_hash_table_size (tree->mark_table) == 0);
g_hash_table_destroy (tree->mark_table);
tree->mark_table = NULL;
if (tree->child_anchor_table != NULL)
if (tree->child_anchor_table != NULL)
{
g_hash_table_destroy (tree->child_anchor_table);
tree->child_anchor_table = NULL;
@@ -568,7 +568,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
start_line_prev = _gtk_text_line_previous (start_line);
end_line = _gtk_text_iter_get_text_line (end);
end_line_next = _gtk_text_line_next (end_line);
line = start_line;
while (line && line != end_line_next)
{
@@ -576,7 +576,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
*/
GtkTextLineSegment *seg = line->segments;
line->dir_strong = PANGO_DIRECTION_NEUTRAL;
while (seg)
{
if (seg->type == &gtk_text_char_type && seg->byte_count > 0)
@@ -584,7 +584,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
PangoDirection pango_dir;
pango_dir = gdk_find_base_dir (seg->body.chars, seg->byte_count);
if (pango_dir != PANGO_DIRECTION_NEUTRAL)
{
line->dir_strong = pango_dir;
@@ -607,7 +607,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
if (start_line_prev)
dir_above_propagated = start_line_prev->dir_propagated_forward;
/* Loop forward and propagate the direction of each paragraph
/* Loop forward and propagate the direction of each paragraph
* to all neutral lines.
*/
line = start_line;
@@ -616,9 +616,9 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
{
if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
last_strong = line->dir_strong;
line->dir_propagated_forward = last_strong;
line = _gtk_text_line_next (line);
}
@@ -627,14 +627,14 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
*/
{
GtkTextIter end_propagate;
while (line &&
line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
line->dir_propagated_forward != last_strong)
{
GtkTextLine *prev = line;
line->dir_propagated_forward = last_strong;
line = _gtk_text_line_next(line);
if (!line)
{
@@ -654,7 +654,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
_gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
_gtk_text_btree_invalidate_region (tree, end, &end_propagate, FALSE);
}
/* Sweep backward */
/* The variable dir_below_propagated contains the backward propagated
@@ -665,7 +665,7 @@ gtk_text_btree_resolve_bidi (GtkTextIter *start,
if (end_line_next)
dir_below_propagated = end_line_next->dir_propagated_back;
/* Loop backward and propagate the direction of each paragraph
/* Loop backward and propagate the direction of each paragraph
* to all neutral lines.
*/
line = end_line;
@@ -929,7 +929,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
BTreeView *view;
GtkTextBTreeNode *ancestor_node;
GtkTextLine *prevline;
int chars_moved;
int chars_moved;
/* last_seg was appended to start_line up at the top of this function */
chars_moved = 0;
@@ -948,7 +948,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
{
node->num_chars += chars_moved;
}
curnode = end_line->parent;
for (node = curnode; node != NULL;
node = node->parent)
@@ -1004,7 +1004,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
if (deleted_width > 0 || deleted_height > 0)
{
ld = _gtk_text_line_get_data (start_line, view->view_id);
if (ld == NULL)
{
/* This means that start_line has never been validated.
@@ -1018,7 +1018,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
ld->height = 0;
ld->valid = FALSE;
}
ld->width = MAX (deleted_width, ld->width);
ld->height += deleted_height;
ld->valid = FALSE;
@@ -1043,7 +1043,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
/* avoid dangling pointer */
deleted_lines = NULL;
gtk_text_btree_rebalance (tree, curnode);
}
@@ -1116,7 +1116,7 @@ _gtk_text_btree_insert (GtkTextIter *iter,
/* extract iterator info */
tree = _gtk_text_iter_get_btree (iter);
line = _gtk_text_iter_get_text_line (iter);
start_line = line;
start_byte_index = gtk_text_iter_get_line_index (iter);
@@ -1131,7 +1131,7 @@ _gtk_text_btree_insert (GtkTextIter *iter,
/* Invalidate all iterators */
chars_changed (tree);
segments_changed (tree);
/*
* Chop the text up into lines and create a new segment for
* each line, plus a new line for the leftovers from the
@@ -1145,11 +1145,11 @@ _gtk_text_btree_insert (GtkTextIter *iter,
while (eol < len)
{
sol = eol;
pango_find_paragraph_boundary (text + sol,
len - sol,
&delim,
&eol);
&eol);
/* make these relative to the start of the text */
delim += sol;
@@ -1160,7 +1160,7 @@ _gtk_text_btree_insert (GtkTextIter *iter,
g_assert (eol >= delim);
g_assert (sol >= 0);
g_assert (eol <= len);
chunk_len = eol - sol;
g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
@@ -1285,13 +1285,13 @@ insert_paintable_or_widget_segment (GtkTextIter *iter,
DV (g_print ("invalidating due to inserting paintable/widget (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree, &start, iter, FALSE);
}
void
_gtk_text_btree_insert_paintable (GtkTextIter *iter,
GdkPaintable *paintable)
{
GtkTextLineSegment *seg;
seg = _gtk_paintable_segment_new (paintable);
seg->body.paintable.tree = _gtk_text_iter_get_btree (iter);
seg->body.paintable.line = _gtk_text_iter_get_text_line (iter);
@@ -1311,12 +1311,12 @@ _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
g_warning (G_STRLOC": Same child anchor can't be inserted twice");
return;
}
seg = _gtk_widget_segment_new (anchor);
tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
seg->body.child.line = _gtk_text_iter_get_text_line (iter);
insert_paintable_or_widget_segment (iter, seg);
if (tree->child_anchor_table == NULL)
@@ -1333,7 +1333,7 @@ _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
GtkTextLineSegment *seg;
seg = anchor->segment;
g_hash_table_remove (seg->body.child.tree->child_anchor_table,
anchor);
}
@@ -1538,7 +1538,7 @@ _gtk_text_btree_add_view (GtkTextBTree *tree,
GtkTextLineData *line_data;
g_return_if_fail (tree != NULL);
view = g_slice_new (BTreeView);
view->view_id = layout;
@@ -1552,7 +1552,7 @@ _gtk_text_btree_add_view (GtkTextBTree *tree,
g_assert (tree->views->prev == NULL);
tree->views->prev = view;
}
tree->views = view;
/* The last line in the buffer has identity values for the per-view
@@ -1580,7 +1580,7 @@ _gtk_text_btree_remove_view (GtkTextBTree *tree,
GtkTextLineData *line_data;
g_return_if_fail (tree != NULL);
view = tree->views;
while (view != NULL)
@@ -1673,7 +1673,7 @@ iter_stack_new (void)
}
static void
iter_stack_push (IterStack *stack,
iter_stack_push (IterStack *stack,
const GtkTextIter *iter)
{
stack->count += 1;
@@ -1687,7 +1687,7 @@ iter_stack_push (IterStack *stack,
}
static gboolean
iter_stack_pop (IterStack *stack,
iter_stack_pop (IterStack *stack,
GtkTextIter *iter)
{
if (stack->count == 0)
@@ -1771,7 +1771,7 @@ _gtk_text_btree_tag (const GtkTextIter *start_orig,
g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
_gtk_text_iter_get_btree (end_orig));
g_return_if_fail (tag->priv->table == _gtk_text_iter_get_btree (start_orig)->table);
#if 0
printf ("%s tag %s from %d to %d\n",
add ? "Adding" : "Removing",
@@ -1885,7 +1885,7 @@ _gtk_text_btree_tag (const GtkTextIter *start_orig,
g_assert (seg != NULL);
g_assert (indexable_seg != NULL);
g_assert (seg != indexable_seg);
if ( (seg->type == &gtk_text_toggle_on_type ||
seg->type == &gtk_text_toggle_off_type) &&
(seg->body.toggle.info == info) )
@@ -2026,7 +2026,7 @@ get_line_internal (GtkTextBTree *tree,
line_count = _gtk_text_btree_line_count (tree);
if (!include_last)
line_count -= 1;
if (line_number < 0)
{
line_number = line_count;
@@ -2387,7 +2387,7 @@ copy_segment (GString *string,
{
gboolean copy = TRUE;
if (!include_nonchars &&
g_strcmp0 (_gtk_text_unknown_char_utf8, seg->body.child.obj->chars) == 0)
g_strcmp0 (_gtk_text_unknown_char_utf8, gtk_text_child_anchor_get_replacement (seg->body.child.obj)) == 0)
{
copy = FALSE;
}
@@ -2400,7 +2400,7 @@ copy_segment (GString *string,
if (copy)
{
g_string_append_len (string,
seg->body.child.obj->chars,
gtk_text_child_anchor_get_replacement (seg->body.child.obj),
seg->byte_count);
}
}
@@ -2741,7 +2741,7 @@ real_set_mark (GtkTextBTree *tree,
/* OK if !should_exist and it does already exist, in that case
* we just move it.
*/
iter = *where;
#ifdef G_ENABLE_DEBUG
@@ -2948,7 +2948,7 @@ _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
segment->body.mark.tree = NULL;
segment->body.mark.line = NULL;
/* Remove the ref on the mark, which frees segment as a side effect
* if this is the last reference.
*/
@@ -2974,7 +2974,7 @@ _gtk_text_btree_remove_mark (GtkTextBTree *tree,
/* This calls cleanup_line and segments_changed */
gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
_gtk_text_btree_release_mark_segment (tree, segment);
}
@@ -3420,12 +3420,12 @@ ensure_end_iter_line (GtkTextBTree *tree)
if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
{
int real_line;
/* n_lines is without the magic line at the end */
g_assert (_gtk_text_btree_line_count (tree) >= 1);
tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
tree->end_iter_line_stamp = tree->chars_changed_stamp;
}
}
@@ -3441,7 +3441,7 @@ ensure_end_iter_segment (GtkTextBTree *tree)
ensure_end_iter_line (tree);
last_with_chars = NULL;
seg = tree->end_iter_line->segments;
while (seg != NULL)
{
@@ -3456,7 +3456,7 @@ ensure_end_iter_segment (GtkTextBTree *tree)
/* We know the last char in the last line is '\n' */
tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
tree->end_iter_segment_stamp = tree->segments_changed_stamp;
g_assert (tree->end_iter_segment->type == &gtk_text_char_type);
@@ -3481,7 +3481,7 @@ _gtk_text_btree_is_end (GtkTextBTree *tree,
int char_offset)
{
g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
/* Do this first to avoid walking segments in most cases */
if (!_gtk_text_line_contains_end_iter (line, tree))
return FALSE;
@@ -3536,7 +3536,7 @@ GtkTextLine*
_gtk_text_line_next_excluding_last (GtkTextLine *line)
{
GtkTextLine *next;
next = _gtk_text_line_next (line);
/* If we were on the end iter line, we can't go to
@@ -3708,9 +3708,9 @@ _gtk_text_line_invalidate_wrap (GtkTextLine *line,
is less than the max width for the parent node,
and the case where the height is unchanged when we re-wrap.
*/
g_return_if_fail (ld != NULL);
ld->valid = FALSE;
gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
}
@@ -4075,7 +4075,7 @@ _gtk_text_line_char_locate (GtkTextLine *line,
g_return_val_if_fail (line != NULL, FALSE);
g_return_val_if_fail (char_offset >= 0, FALSE);
*segment = NULL;
*any_segment = NULL;
chars_in_line = 0;
@@ -4216,7 +4216,7 @@ _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
/* if in the last fourth of the segment walk backwards */
if (seg->char_count - offset < seg->char_count / 4)
p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
offset - seg->char_count);
else
p = g_utf8_offset_to_pointer (seg->body.chars, offset);
@@ -4357,7 +4357,7 @@ _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
/* Our tag summaries only have node precision, not line
* precision. This means that if any line under a node could contain a
* tag, then any of the others could also contain a tag.
*
*
* In the future we could have some mechanism to keep track of how
* many toggles we've found under a node so far, since we have a
* count of toggles under the node. But for now I'm going with KISS.
@@ -4828,7 +4828,7 @@ node_data_new (gpointer view_id,
NodeData *next)
{
NodeData *nd;
nd = g_slice_new (NodeData);
nd->view_id = view_id;
@@ -4853,7 +4853,7 @@ node_data_list_destroy (NodeData *nd)
}
static inline NodeData*
node_data_find (NodeData *nd,
node_data_find (NodeData *nd,
gpointer view_id)
{
while (nd != NULL)
@@ -5439,7 +5439,7 @@ _gtk_text_btree_validate_line (GtkTextBTree *tree,
view = gtk_text_btree_get_view (tree, view_id);
g_return_if_fail (view != NULL);
ld = _gtk_text_line_get_data (line, view_id);
if (!ld || !ld->valid)
{
@@ -6208,7 +6208,7 @@ recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
gtk_text_btree_node_check_valid (node, view->view_id);
view = view->next;
}
/*
* Scan through the GtkTextBTreeNodes tag records again and delete any Summary
* records that still have a zero count, or that have all the toggles.
@@ -6617,7 +6617,7 @@ gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
int height;
gboolean valid;
BTreeView *view;
view = tree->views;
while (view != NULL)
@@ -6631,7 +6631,7 @@ gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
if (view == NULL)
g_error ("Node has data for a view %p no longer attached to the tree",
nd->view_id);
gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
&width, &height, &valid);
@@ -6644,7 +6644,7 @@ gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
* invalid - we dont guarantee that if the node is invalid there
* are invalid lines.
*/
if (nd->width != width ||
nd->height != height ||
(nd->valid && !valid))
@@ -7142,7 +7142,7 @@ _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
}
else if (seg->type == &gtk_text_child_type)
{
char *str = g_strndup (seg->body.child.obj->chars, seg->byte_count);
char *str = g_strndup (gtk_text_child_anchor_get_replacement (seg->body.child.obj), seg->byte_count);
printf ("%s child '%s'...\n", spaces, str);
g_free (str);
}
@@ -7250,7 +7250,7 @@ _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
}
else if (seg->type == &gtk_text_child_type)
{
char *str = g_strndup (seg->body.child.obj->chars, seg->byte_count);
char *str = g_strndup (gtk_text_child_anchor_get_replacement (seg->body.child.obj), seg->byte_count);
printf (" '%s'\n", str);
g_free (str);
}
+56 -37
View File
@@ -53,6 +53,12 @@
#include "gtktextlayoutprivate.h"
#include "gtkintl.h"
typedef struct {
char *replacement;
} GtkTextChildAnchorPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GtkTextChildAnchor, gtk_text_child_anchor, G_TYPE_OBJECT)
#define CHECK_IN_BUFFER(anchor) \
G_STMT_START { \
if ((anchor)->segment == NULL) \
@@ -232,7 +238,7 @@ child_segment_delete_func (GtkTextLineSegment *seg,
GSList *copy;
_gtk_text_btree_unregister_child_anchor (seg->body.child.obj);
seg->body.child.tree = NULL;
seg->body.child.line = NULL;
@@ -254,9 +260,9 @@ child_segment_delete_func (GtkTextLineSegment *seg,
g_assert (seg->body.child.widgets == NULL);
g_slist_free (copy);
_gtk_widget_segment_unref (seg);
_gtk_widget_segment_unref (seg);
return 0;
}
@@ -291,6 +297,7 @@ _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
GtkTextLineSegment *seg;
GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (anchor);
seg = g_slice_alloc (WIDGET_SEG_SIZE);
@@ -298,8 +305,8 @@ _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
seg->next = NULL;
seg->byte_count = strlen (anchor->chars);
seg->char_count = g_utf8_strlen (anchor->chars, seg->byte_count);
seg->byte_count = strlen (priv->replacement);
seg->char_count = g_utf8_strlen (priv->replacement, seg->byte_count);
seg->body.child.obj = anchor;
seg->body.child.obj->segment = seg;
@@ -308,7 +315,7 @@ _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
seg->body.child.line = NULL;
g_object_ref (anchor);
return seg;
#pragma GCC diagnostic pop
}
@@ -321,7 +328,7 @@ _gtk_widget_segment_add (GtkTextLineSegment *widget_segment,
g_return_if_fail (widget_segment->body.child.tree != NULL);
g_object_ref (child);
widget_segment->body.child.widgets =
g_slist_prepend (widget_segment->body.child.widgets,
child);
@@ -332,7 +339,7 @@ _gtk_widget_segment_remove (GtkTextLineSegment *widget_segment,
GtkWidget *child)
{
g_return_if_fail (widget_segment->type == &gtk_text_child_type);
widget_segment->body.child.widgets =
g_slist_remove (widget_segment->body.child.widgets,
child);
@@ -359,7 +366,7 @@ _gtk_widget_segment_unref (GtkTextLineSegment *widget_segment)
GtkTextLayout*
_gtk_anchored_child_get_layout (GtkWidget *child)
{
return g_object_get_data (G_OBJECT (child), "gtk-text-child-anchor-layout");
return g_object_get_data (G_OBJECT (child), "gtk-text-child-anchor-layout");
}
static void
@@ -368,12 +375,10 @@ _gtk_anchored_child_set_layout (GtkWidget *child,
{
g_object_set_data (G_OBJECT (child),
I_("gtk-text-child-anchor-layout"),
layout);
layout);
}
static void gtk_text_child_anchor_finalize (GObject *obj);
G_DEFINE_TYPE (GtkTextChildAnchor, gtk_text_child_anchor, G_TYPE_OBJECT)
static void gtk_text_child_anchor_finalize (GObject *obj);
static void
gtk_text_child_anchor_init (GtkTextChildAnchor *child_anchor)
@@ -391,14 +396,14 @@ gtk_text_child_anchor_class_init (GtkTextChildAnchorClass *klass)
/**
* gtk_text_child_anchor_new:
*
*
* Creates a new `GtkTextChildAnchor`.
*
* Usually you would then insert it into a `GtkTextBuffer` with
* [method@Gtk.TextBuffer.insert_child_anchor]. To perform the
* creation and insertion in one step, use the convenience
* function [method@Gtk.TextBuffer.create_child_anchor].
*
*
* Returns: a new `GtkTextChildAnchor`
**/
GtkTextChildAnchor*
@@ -416,28 +421,34 @@ gtk_text_child_anchor_new (void)
* [method@Gtk.TextBuffer.insert_child_anchor].
*
* Returns: a new `GtkTextChildAnchor`
*
* Since: 4.6
**/
GtkTextChildAnchor *
gtk_text_child_anchor_new_with_replacement (const char *replacement_character)
{
GtkTextChildAnchor *anchor;
GtkTextChildAnchorPrivate *priv;
/* only a single character can be set as replacement */
g_return_val_if_fail (g_utf8_strlen (replacement_character, -1) == 1, NULL);
GtkTextChildAnchor *anchor = g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
anchor->chars = g_strdup (replacement_character);
anchor = g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
priv = gtk_text_child_anchor_get_instance_private (anchor);
priv->replacement = g_strdup (replacement_character);
return anchor;
}
static void
gtk_text_child_anchor_finalize (GObject *obj)
{
GtkTextChildAnchor *anchor;
GtkTextLineSegment *seg;
anchor = GTK_TEXT_CHILD_ANCHOR (obj);
GtkTextChildAnchor *anchor = GTK_TEXT_CHILD_ANCHOR (obj);
GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (anchor);
GtkTextLineSegment *seg = anchor->segment;
seg = anchor->segment;
if (seg)
{
if (seg->body.child.tree != NULL)
@@ -453,7 +464,7 @@ gtk_text_child_anchor_finalize (GObject *obj)
g_slice_free1 (WIDGET_SEG_SIZE, seg);
}
anchor->segment = NULL;
g_free (priv->replacement);
G_OBJECT_CLASS (gtk_text_child_anchor_parent_class)->finalize (obj);
}
@@ -462,7 +473,7 @@ gtk_text_child_anchor_finalize (GObject *obj)
* gtk_text_child_anchor_get_widgets:
* @anchor: a `GtkTextChildAnchor`
* @out_len: (out): return location for the length of the array
*
*
* Gets a list of all widgets anchored at this child anchor.
*
* The order in which the widgets are returned is not defined.
@@ -527,7 +538,7 @@ gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor)
GtkTextLineSegment *seg = anchor->segment;
CHECK_IN_BUFFER_RETURN (anchor, TRUE);
g_return_val_if_fail (seg->type == &gtk_text_child_type, TRUE);
return seg->body.child.tree == NULL;
@@ -542,9 +553,9 @@ gtk_text_child_anchor_register_child (GtkTextChildAnchor *anchor,
g_return_if_fail (GTK_IS_WIDGET (child));
CHECK_IN_BUFFER (anchor);
_gtk_anchored_child_set_layout (child, layout);
_gtk_widget_segment_add (anchor->segment, child);
gtk_text_child_anchor_queue_resize (anchor, layout);
@@ -558,15 +569,15 @@ gtk_text_child_anchor_unregister_child (GtkTextChildAnchor *anchor,
g_return_if_fail (GTK_IS_WIDGET (child));
CHECK_IN_BUFFER (anchor);
if (_gtk_anchored_child_get_layout (child))
{
gtk_text_child_anchor_queue_resize (anchor,
_gtk_anchored_child_get_layout (child));
}
_gtk_anchored_child_set_layout (child, NULL);
_gtk_widget_segment_remove (anchor->segment, child);
}
@@ -577,22 +588,22 @@ gtk_text_child_anchor_queue_resize (GtkTextChildAnchor *anchor,
GtkTextIter start;
GtkTextIter end;
GtkTextLineSegment *seg;
g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
CHECK_IN_BUFFER (anchor);
seg = anchor->segment;
if (seg->body.child.tree == NULL)
return;
gtk_text_buffer_get_iter_at_child_anchor (layout->buffer,
&start, anchor);
end = start;
gtk_text_iter_forward_char (&end);
gtk_text_layout_invalidate (layout, &start, &end);
}
@@ -602,6 +613,14 @@ gtk_text_anchored_child_set_layout (GtkWidget *child,
{
g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (layout == NULL || GTK_IS_TEXT_LAYOUT (layout));
_gtk_anchored_child_set_layout (child, layout);
}
const char *
gtk_text_child_anchor_get_replacement (GtkTextChildAnchor *anchor)
{
GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (anchor);
return priv->replacement;
}
-1
View File
@@ -60,7 +60,6 @@ struct _GtkTextChildAnchor
/*< private >*/
gpointer segment;
char *chars; /* replacement character */
};
struct _GtkTextChildAnchorClass
+2
View File
@@ -85,6 +85,8 @@ void _gtk_widget_segment_unref (GtkTextLineSegment *widget_seg
GtkTextLayout* _gtk_anchored_child_get_layout (GtkWidget *child);
const char * gtk_text_child_anchor_get_replacement (GtkTextChildAnchor *anchor);
G_END_DECLS
#endif
+101 -101
View File
@@ -294,7 +294,7 @@ iter_init_from_byte_offset (GtkTextIter *iter,
"character; this will crash the text buffer. "
"Byte indexes must refer to the start of a character.",
line_byte_offset);
return real;
}
@@ -611,11 +611,11 @@ gtk_text_iter_get_offset (const GtkTextIter *iter)
return 0;
check_invariants (iter);
if (real->cached_char_index < 0)
{
ensure_char_offsets (real);
real->cached_char_index =
_gtk_text_line_char_index (real->line);
real->cached_char_index += real->line_char_offset;
@@ -705,7 +705,7 @@ int
gtk_text_iter_get_line_index (const GtkTextIter *iter)
{
GtkTextRealIter *real;
g_return_val_if_fail (iter != NULL, 0);
real = gtk_text_iter_make_surreal (iter);
@@ -738,7 +738,7 @@ gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter)
int vis_offset;
GtkTextLineSegment *seg;
GtkTextIter pos;
g_return_val_if_fail (iter != NULL, 0);
real = gtk_text_iter_make_real (iter);
@@ -749,11 +749,11 @@ gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter)
ensure_char_offsets (real);
check_invariants (iter);
vis_offset = real->line_char_offset;
g_assert (vis_offset >= 0);
_gtk_text_btree_get_iter_at_line (real->tree,
&pos,
real->line,
@@ -780,7 +780,7 @@ gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter)
if (_gtk_text_btree_char_is_invisible (&pos))
vis_offset -= real->segment_char_offset;
return vis_offset;
}
@@ -803,7 +803,7 @@ gtk_text_iter_get_visible_line_index (const GtkTextIter *iter)
int vis_offset;
GtkTextLineSegment *seg;
GtkTextIter pos;
g_return_val_if_fail (iter != NULL, 0);
real = gtk_text_iter_make_real (iter);
@@ -818,7 +818,7 @@ gtk_text_iter_get_visible_line_index (const GtkTextIter *iter)
vis_offset = real->line_byte_offset;
g_assert (vis_offset >= 0);
_gtk_text_btree_get_iter_at_line (real->tree,
&pos,
real->line,
@@ -845,7 +845,7 @@ gtk_text_iter_get_visible_line_index (const GtkTextIter *iter)
if (_gtk_text_btree_char_is_invisible (&pos))
vis_offset -= real->segment_byte_offset;
return vis_offset;
}
@@ -888,13 +888,13 @@ gtk_text_iter_get_char (const GtkTextIter *iter)
else if (real->segment->type == &gtk_text_char_type)
{
ensure_byte_offsets (real);
return g_utf8_get_char (real->segment->body.chars +
real->segment_byte_offset);
}
else if (real->segment->type == &gtk_text_child_type)
{
return g_utf8_get_char (real->segment->body.child.obj->chars);
return g_utf8_get_char (gtk_text_child_anchor_get_replacement (real->segment->body.child.obj));
}
else
{
@@ -1445,7 +1445,7 @@ gtk_text_iter_editable (const GtkTextIter *iter,
gboolean retval;
g_return_val_if_fail (iter != NULL, FALSE);
values = gtk_text_attributes_new ();
values->editable = default_setting;
@@ -1480,7 +1480,7 @@ gtk_text_iter_can_insert (const GtkTextIter *iter,
gboolean default_editability)
{
g_return_val_if_fail (iter != NULL, FALSE);
if (gtk_text_iter_editable (iter, default_editability))
return TRUE;
/* If at start/end of buffer, default editability is used */
@@ -1540,7 +1540,7 @@ gtk_text_iter_get_language (const GtkTextIter *iter)
{
GtkTextAttributes *values;
PangoLanguage *retval;
values = gtk_text_attributes_new ();
gtk_text_iter_get_attributes (iter, values);
@@ -1613,7 +1613,7 @@ gboolean
gtk_text_iter_ends_line (const GtkTextIter *iter)
{
gunichar wc;
g_return_val_if_fail (iter != NULL, FALSE);
check_invariants (iter);
@@ -1624,7 +1624,7 @@ gtk_text_iter_ends_line (const GtkTextIter *iter)
#define PARAGRAPH_SEPARATOR 0x2029
wc = gtk_text_iter_get_char (iter);
if (wc == '\r' || wc == PARAGRAPH_SEPARATOR || wc == 0) /* wc == 0 is end iterator */
return TRUE;
else if (wc == '\n')
@@ -1689,7 +1689,7 @@ gtk_text_iter_is_end (const GtkTextIter *iter)
if (real == NULL)
return FALSE;
return _gtk_text_btree_is_end (real->tree, real->line,
real->segment,
real->segment_byte_offset,
@@ -1758,7 +1758,7 @@ gtk_text_iter_get_chars_in_line (const GtkTextIter *iter)
if (_gtk_text_line_contains_end_iter (real->line, real->tree))
count -= 1; /* Dump the newline that was in the last segment of the end iter line */
return count;
}
@@ -1809,7 +1809,7 @@ gtk_text_iter_get_bytes_in_line (const GtkTextIter *iter)
if (_gtk_text_line_contains_end_iter (real->line, real->tree))
count -= 1; /* Dump the newline that was in the last segment of the end iter line */
return count;
}
@@ -1831,12 +1831,12 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
if (!_gtk_text_line_contains_end_iter (real->line, real->tree))
{
GtkTextLine *new_line;
new_line = _gtk_text_line_next (real->line);
g_assert (new_line);
g_assert (new_line != real->line);
g_assert (!_gtk_text_line_is_last (new_line, real->tree));
real->line = new_line;
real->line_byte_offset = 0;
@@ -1859,7 +1859,7 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
* the line containing the end iterator.
* However we may not be at the end iterator itself.
*/
return FALSE;
}
}
@@ -1912,7 +1912,7 @@ backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
return FALSE;
}
}
#endif
#endif
/* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
* DEREFERENCEABLE)
@@ -2064,7 +2064,7 @@ _gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
/* End of buffer, but iter is still at start of last segment,
* not at the end iterator. We put it on the end iterator.
*/
check_invariants (iter);
g_assert (!_gtk_text_line_is_last (real->line, real->tree));
@@ -2073,7 +2073,7 @@ _gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
gtk_text_iter_forward_to_line_end (iter);
g_assert (gtk_text_iter_is_end (iter));
return FALSE;
}
}
@@ -2326,7 +2326,7 @@ gtk_text_iter_forward_chars (GtkTextIter *iter, int count)
g_return_val_if_fail (iter != NULL, FALSE);
FIX_OVERFLOWS (count);
real = gtk_text_iter_make_real (iter);
if (real == NULL)
@@ -2400,7 +2400,7 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, int count)
g_return_val_if_fail (iter != NULL, FALSE);
FIX_OVERFLOWS (count);
real = gtk_text_iter_make_real (iter);
if (real == NULL)
@@ -2430,7 +2430,7 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, int count)
/* if in the last fourth of the segment walk backwards */
if (count < real->segment_char_offset / 4)
p = g_utf8_offset_to_pointer (real->segment->body.chars + real->segment_byte_offset,
p = g_utf8_offset_to_pointer (real->segment->body.chars + real->segment_byte_offset,
-count);
else
p = g_utf8_offset_to_pointer (real->segment->body.chars,
@@ -2555,7 +2555,7 @@ gtk_text_iter_forward_line (GtkTextIter *iter)
GtkTextRealIter *real;
g_return_val_if_fail (iter != NULL, FALSE);
real = gtk_text_iter_make_real (iter);
if (real == NULL)
@@ -2578,10 +2578,10 @@ gtk_text_iter_forward_line (GtkTextIter *iter)
else
{
/* On the last line, move to end of it */
if (!gtk_text_iter_is_end (iter))
gtk_text_iter_forward_to_end (iter);
check_invariants (iter);
return FALSE;
}
@@ -2689,7 +2689,7 @@ gboolean
gtk_text_iter_forward_lines (GtkTextIter *iter, int count)
{
FIX_OVERFLOWS (count);
if (count < 0)
return gtk_text_iter_backward_lines (iter, 0 - count);
else if (count == 0)
@@ -2705,7 +2705,7 @@ gtk_text_iter_forward_lines (GtkTextIter *iter, int count)
if (gtk_text_iter_is_end (iter))
return FALSE;
old_line = gtk_text_iter_get_line (iter);
gtk_text_iter_set_line (iter, old_line + count);
@@ -2716,7 +2716,7 @@ gtk_text_iter_forward_lines (GtkTextIter *iter, int count)
if (!gtk_text_iter_is_end (iter))
gtk_text_iter_forward_to_end (iter);
}
return !gtk_text_iter_is_end (iter);
}
}
@@ -2743,7 +2743,7 @@ gboolean
gtk_text_iter_backward_lines (GtkTextIter *iter, int count)
{
FIX_OVERFLOWS (count);
if (count < 0)
return gtk_text_iter_forward_lines (iter, 0 - count);
else if (count == 0)
@@ -2790,14 +2790,14 @@ gtk_text_iter_forward_visible_line (GtkTextIter *iter)
{
if (!gtk_text_iter_forward_char (iter))
return FALSE;
if (!_gtk_text_btree_char_is_invisible (iter))
return TRUE;
}
while (!gtk_text_iter_ends_line (iter));
}
}
return FALSE;
}
@@ -2830,14 +2830,14 @@ gtk_text_iter_backward_visible_line (GtkTextIter *iter)
{
if (!gtk_text_iter_backward_char (iter))
return FALSE;
if (!_gtk_text_btree_char_is_invisible (iter))
return TRUE;
}
while (!gtk_text_iter_starts_line (iter));
}
}
return FALSE;
}
@@ -2864,7 +2864,7 @@ gtk_text_iter_forward_visible_lines (GtkTextIter *iter,
int count)
{
FIX_OVERFLOWS (count);
if (count < 0)
return gtk_text_iter_backward_visible_lines (iter, 0 - count);
else if (count == 0)
@@ -2879,7 +2879,7 @@ gtk_text_iter_forward_visible_lines (GtkTextIter *iter,
while (gtk_text_iter_forward_visible_line (iter) && count > 0)
count--;
return count == 0;
}
}
}
/**
@@ -2905,7 +2905,7 @@ gtk_text_iter_backward_visible_lines (GtkTextIter *iter,
int count)
{
FIX_OVERFLOWS (count);
if (count < 0)
return gtk_text_iter_forward_visible_lines (iter, 0 - count);
else if (count == 0)
@@ -3138,9 +3138,9 @@ find_line_log_attrs (const GtkTextIter *iter,
int offset;
g_return_val_if_fail (iter != NULL, FALSE);
attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
iter, &char_len);
iter, &char_len);
offset = gtk_text_iter_get_line_offset (iter);
@@ -3244,9 +3244,9 @@ find_visible_by_log_attrs (GtkTextIter *iter,
typedef gboolean (* OneStepFunc) (GtkTextIter *iter);
typedef gboolean (* MultipleStepFunc) (GtkTextIter *iter, int count);
static gboolean
move_multiple_steps (GtkTextIter *iter,
static gboolean
move_multiple_steps (GtkTextIter *iter,
int count,
OneStepFunc step_forward,
MultipleStepFunc n_steps_backward)
@@ -3254,13 +3254,13 @@ move_multiple_steps (GtkTextIter *iter,
g_return_val_if_fail (iter != NULL, FALSE);
FIX_OVERFLOWS (count);
if (count == 0)
return FALSE;
if (count < 0)
return n_steps_backward (iter, -count);
if (!step_forward (iter))
return FALSE;
--count;
@@ -3271,10 +3271,10 @@ move_multiple_steps (GtkTextIter *iter,
break;
--count;
}
return !gtk_text_iter_is_end (iter);
return !gtk_text_iter_is_end (iter);
}
/**
* gtk_text_iter_forward_word_end:
@@ -3333,7 +3333,7 @@ gboolean
gtk_text_iter_forward_word_ends (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_forward_word_end,
gtk_text_iter_backward_word_starts);
}
@@ -3351,7 +3351,7 @@ gboolean
gtk_text_iter_backward_word_starts (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_backward_word_start,
gtk_text_iter_forward_word_ends);
}
@@ -3409,7 +3409,7 @@ gboolean
gtk_text_iter_forward_visible_word_ends (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_forward_visible_word_end,
gtk_text_iter_backward_visible_word_starts);
}
@@ -3427,7 +3427,7 @@ gboolean
gtk_text_iter_backward_visible_word_starts (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_backward_visible_word_start,
gtk_text_iter_forward_visible_word_ends);
}
@@ -3599,7 +3599,7 @@ gboolean
gtk_text_iter_forward_sentence_ends (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_forward_sentence_end,
gtk_text_iter_backward_sentence_starts);
}
@@ -3619,7 +3619,7 @@ gboolean
gtk_text_iter_backward_sentence_starts (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_backward_sentence_start,
gtk_text_iter_forward_sentence_ends);
}
@@ -3738,7 +3738,7 @@ gboolean
gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_forward_cursor_position,
gtk_text_iter_backward_cursor_positions);
}
@@ -3758,7 +3758,7 @@ gboolean
gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_backward_cursor_position,
gtk_text_iter_forward_cursor_positions);
}
@@ -3810,7 +3810,7 @@ gboolean
gtk_text_iter_forward_visible_cursor_positions (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_forward_visible_cursor_position,
gtk_text_iter_backward_visible_cursor_positions);
}
@@ -3830,7 +3830,7 @@ gboolean
gtk_text_iter_backward_visible_cursor_positions (GtkTextIter *iter,
int count)
{
return move_multiple_steps (iter, count,
return move_multiple_steps (iter, count,
gtk_text_iter_backward_visible_cursor_position,
gtk_text_iter_forward_visible_cursor_positions);
}
@@ -3871,14 +3871,14 @@ gtk_text_iter_set_line_offset (GtkTextIter *iter,
{
GtkTextRealIter *real;
int chars_in_line;
g_return_if_fail (iter != NULL);
real = gtk_text_iter_make_surreal (iter);
if (real == NULL)
return;
check_invariants (iter);
chars_in_line = gtk_text_iter_get_chars_in_line (iter);
@@ -3889,7 +3889,7 @@ gtk_text_iter_set_line_offset (GtkTextIter *iter,
iter_set_from_char_offset (real, real->line, char_on_line);
else
gtk_text_iter_forward_line (iter); /* set to start of next line */
check_invariants (iter);
}
@@ -3909,7 +3909,7 @@ gtk_text_iter_set_line_index (GtkTextIter *iter,
{
GtkTextRealIter *real;
int bytes_in_line;
g_return_if_fail (iter != NULL);
real = gtk_text_iter_make_surreal (iter);
@@ -3922,7 +3922,7 @@ gtk_text_iter_set_line_index (GtkTextIter *iter,
bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
g_return_if_fail (byte_on_line <= bytes_in_line);
if (byte_on_line < bytes_in_line)
iter_set_from_byte_offset (real, real->line, byte_on_line);
else
@@ -3956,7 +3956,7 @@ gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
GtkTextIter pos;
g_return_if_fail (iter != NULL);
gtk_text_iter_set_line_offset (iter, 0);
pos = *iter;
@@ -3973,7 +3973,7 @@ gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
if (chars_seen == char_on_line)
break;
}
if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
*iter = pos;
else
@@ -3997,7 +3997,7 @@ gtk_text_iter_set_visible_line_index (GtkTextIter *iter,
int offset = 0;
GtkTextIter pos;
GtkTextLineSegment *seg;
g_return_if_fail (iter != NULL);
gtk_text_iter_set_line_offset (iter, 0);
@@ -4204,12 +4204,12 @@ gtk_text_iter_forward_to_line_end (GtkTextIter *iter)
int current_offset;
int new_offset;
g_return_val_if_fail (iter != NULL, FALSE);
current_offset = gtk_text_iter_get_line_offset (iter);
new_offset = find_paragraph_delimiter_for_line (iter);
if (current_offset < new_offset)
{
/* Move to end of this line. */
@@ -4462,11 +4462,11 @@ gtk_text_iter_forward_find_char (GtkTextIter *iter,
if (limit &&
gtk_text_iter_compare (iter, limit) >= 0)
return FALSE;
while ((limit == NULL ||
!gtk_text_iter_equal (limit, iter)) &&
gtk_text_iter_forward_char (iter))
{
{
if (matches_pred (iter, pred, user_data))
return TRUE;
}
@@ -4498,7 +4498,7 @@ gtk_text_iter_backward_find_char (GtkTextIter *iter,
if (limit &&
gtk_text_iter_compare (iter, limit) <= 0)
return FALSE;
while ((limit == NULL ||
!gtk_text_iter_equal (limit, iter)) &&
gtk_text_iter_backward_char (iter))
@@ -4998,18 +4998,18 @@ gtk_text_iter_forward_search (const GtkTextIter *iter,
if (limit &&
gtk_text_iter_compare (iter, limit) >= 0)
return FALSE;
if (*str == '\0')
{
/* If we can move one char, return the empty string there */
match = *iter;
if (gtk_text_iter_forward_char (&match))
{
if (limit &&
gtk_text_iter_equal (&match, limit))
return FALSE;
if (match_start)
*match_start = match;
if (match_end)
@@ -5041,7 +5041,7 @@ gtk_text_iter_forward_search (const GtkTextIter *iter,
if (limit &&
gtk_text_iter_compare (&search, limit) >= 0)
break;
if (lines_match (&search, (const char **)lines,
visible_only, slice, case_insensitive, &match, &end))
{
@@ -5050,14 +5050,14 @@ gtk_text_iter_forward_search (const GtkTextIter *iter,
gtk_text_iter_compare (&end, limit) <= 0))
{
retval = TRUE;
if (match_start)
*match_start = match;
if (match_end)
*match_end = end;
}
break;
}
}
@@ -5316,14 +5316,14 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
gboolean visible_only;
gboolean slice;
gboolean case_insensitive;
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (str != NULL, FALSE);
if (limit &&
gtk_text_iter_compare (limit, iter) > 0)
return FALSE;
if (*str == '\0')
{
/* If we can move one char, return the empty string there */
@@ -5331,7 +5331,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
if (limit && gtk_text_iter_equal (limit, &match))
return FALSE;
if (gtk_text_iter_backward_char (&match))
{
if (match_start)
@@ -5371,7 +5371,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
/* We're now before the search limit, abort. */
goto out;
}
/* If there are multiple lines, the first line will
* end in '\n', so this will only match at the
* end of the first line, which is correct.
@@ -5425,7 +5425,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
out:
lines_window_free (&win);
g_strfreev (lines);
return retval;
}
@@ -5437,7 +5437,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
* gtk_text_iter_equal:
* @lhs: a `GtkTextIter`
* @rhs: another `GtkTextIter`
*
*
* Tests whether two iterators are equal, using the fastest possible
* mechanism.
*
@@ -5445,7 +5445,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
* better than e.g. getting the character offset for each
* iterator and comparing the offsets yourself. Also, its a
* bit faster than [method@Gtk.TextIter.compare].
*
*
* Returns: %TRUE if the iterators point to the same place in the buffer
**/
gboolean
@@ -5524,7 +5524,7 @@ gtk_text_iter_compare (const GtkTextIter *lhs,
check_invariants (lhs);
check_invariants (rhs);
if (real_lhs->line == real_rhs->line)
{
int left_index, right_index;
@@ -5588,7 +5588,7 @@ gtk_text_iter_in_range (const GtkTextIter *iter,
g_return_val_if_fail (start != NULL, FALSE);
g_return_val_if_fail (end != NULL, FALSE);
g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE);
return gtk_text_iter_compare (iter, start) >= 0 &&
gtk_text_iter_compare (iter, end) < 0;
}
@@ -5666,7 +5666,7 @@ _gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
g_return_if_fail (tree != NULL);
line = _gtk_text_btree_get_line_no_last (tree, line_number, &real_line);
iter_init_from_char_offset (iter, tree, line, char_on_line);
/* We might as well cache this, since we know it. */
@@ -5762,7 +5762,7 @@ _gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree,
found = gtk_text_iter_backward_to_tag_toggle (iter, tag);
check_invariants (iter);
return found;
}
@@ -5831,11 +5831,11 @@ _gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree *tree,
g_return_if_fail (iter != NULL);
g_return_if_fail (tree != NULL);
g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
seg = anchor->segment;
seg = anchor->segment;
g_assert (seg->body.child.line != NULL);
iter_init_from_segment (iter, tree,
seg->body.child.line, seg);
g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter));
@@ -5934,7 +5934,7 @@ _gtk_text_iter_check (const GtkTextIter *iter)
{
const char *p;
p = byte_segment->body.chars + seg_byte_offset;
if (!gtk_text_byte_begins_utf8_char (p))
g_error ("broken iterator byte index pointed into the middle of a character");
}
@@ -5951,7 +5951,7 @@ _gtk_text_iter_check (const GtkTextIter *iter)
g_error ("wrong char offset was stored in iterator");
if (segments_updated)
{
{
if (real->segment != char_segment)
g_error ("wrong segment was stored in iterator");
+95 -95
View File
@@ -454,7 +454,7 @@ gtk_text_layout_set_overwrite_mode (GtkTextLayout *layout,
* %GTK_TEXT_DIR_NONE means draw cursors for both
* left-to-right insertion and right-to-left insertion.
* (The two cursors will be visually distinguished.)
*
*
* Sets which text directions (left-to-right and/or right-to-left) for
* which cursors will be drawn for the insertion point. The visual
* point at which new text is inserted depends on whether the new
@@ -580,7 +580,7 @@ gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
* @preedit_string: a string to display at the insertion point
* @preedit_attrs: a `PangoAttrList` of attributes that apply to @preedit_string
* @cursor_pos: position of cursor within preedit string in chars
*
*
* Set the preedit string and attributes. The preedit string is a
* string showing text that is currently being edited and not
* yet committed into the buffer.
@@ -796,7 +796,7 @@ gtk_text_layout_invalidate (GtkTextLayout *layout,
* we always invalidate the line with "start" even
* if there's an empty range.
*/
#if 0
gtk_text_view_index_spew (start_index, "invalidate start");
gtk_text_view_index_spew (end_index, "invalidate end");
@@ -810,7 +810,7 @@ gtk_text_layout_invalidate (GtkTextLayout *layout,
GtkTextLineData *line_data = _gtk_text_line_get_data (line, layout);
gtk_text_layout_invalidate_cache (layout, line, FALSE);
if (line_data)
_gtk_text_line_invalidate_wrap (line, line_data);
@@ -930,7 +930,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout,
{
int old_height, new_height;
int top_ink, bottom_ink;
old_height = line_data ? line_data->height : 0;
top_ink = line_data ? line_data->top_ink : 0;
bottom_ink = line_data ? line_data->bottom_ink : 0;
@@ -947,7 +947,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout,
}
delta_height += new_height - old_height;
first_line = line;
first_line_y = -seen - new_height - top_ink;
if (!last_line)
@@ -971,7 +971,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout,
{
int old_height, new_height;
int top_ink, bottom_ink;
old_height = line_data ? line_data->height : 0;
top_ink = line_data ? line_data->top_ink : 0;
bottom_ink = line_data ? line_data->bottom_ink : 0;
@@ -987,7 +987,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout,
}
delta_height += new_height - old_height;
if (!first_line)
{
first_line = line;
@@ -1062,7 +1062,7 @@ gtk_text_layout_wrap (GtkTextLayout *layout,
g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
g_return_val_if_fail (line != NULL, NULL);
if (line_data == NULL)
{
line_data = _gtk_text_line_data_new (layout, line);
@@ -1159,9 +1159,9 @@ totally_invisible_line (GtkTextLayout *layout,
GtkTextLineSegment *seg;
int bytes = 0;
/* Check if the first char is visible, if so we are partially visible.
* Note that we have to check this since we don't know the current
* invisible/noninvisible toggle state; this function can use the whole btree
/* Check if the first char is visible, if so we are partially visible.
* Note that we have to check this since we don't know the current
* invisible/noninvisible toggle state; this function can use the whole btree
* to get it right.
*/
gtk_text_layout_get_iter_at_line (layout, iter, line, 0);
@@ -1233,7 +1233,7 @@ set_para_values (GtkTextLayout *layout,
base_dir = PANGO_DIRECTION_RTL;
else
base_dir = PANGO_DIRECTION_LTR;
break;
case PANGO_DIRECTION_RTL :
display->direction = GTK_TEXT_DIR_RTL;
@@ -1247,7 +1247,7 @@ set_para_values (GtkTextLayout *layout,
display->direction = GTK_TEXT_DIR_LTR;
break;
}
if (display->direction == GTK_TEXT_DIR_RTL)
display->layout = pango_layout_new (layout->rtl_context);
else
@@ -1285,7 +1285,7 @@ set_para_values (GtkTextLayout *layout,
display->bottom_margin = style->pixels_below_lines;
display->left_margin = style->left_margin;
display->right_margin = style->right_margin;
display->x_offset = display->left_margin;
pango_layout_set_indent (display->layout,
@@ -1362,12 +1362,12 @@ gtk_text_attr_appearance_destroy (PangoAttribute *attr)
g_slice_free (GtkTextAttrAppearance, appearance_attr);
}
static gboolean
static gboolean
rgba_equal (const GdkRGBA *rgba1, const GdkRGBA *rgba2)
{
if (rgba1 && rgba2)
return gdk_rgba_equal (rgba1, rgba2);
if (rgba1 || rgba2)
return FALSE;
@@ -1479,10 +1479,10 @@ add_generic_attrs (GtkTextLayout *layout,
if (appearance->underline != PANGO_UNDERLINE_NONE)
{
attr = pango_attr_underline_new (appearance->underline);
attr->start_index = start;
attr->end_index = start + byte_count;
pango_attr_list_insert (attrs, attr);
}
@@ -1523,10 +1523,10 @@ add_generic_attrs (GtkTextLayout *layout,
if (appearance->strikethrough)
{
attr = pango_attr_strikethrough_new (appearance->strikethrough);
attr->start_index = start;
attr->end_index = start + byte_count;
pango_attr_list_insert (attrs, attr);
}
@@ -1545,22 +1545,22 @@ add_generic_attrs (GtkTextLayout *layout,
if (appearance->rise != 0)
{
attr = pango_attr_rise_new (appearance->rise);
attr->start_index = start;
attr->end_index = start + byte_count;
pango_attr_list_insert (attrs, attr);
}
if (!size_only)
{
attr = gtk_text_attr_appearance_new (appearance);
attr->start_index = start;
attr->end_index = start + byte_count;
((GtkTextAttrAppearance *)attr)->appearance.is_text = is_text;
pango_attr_list_insert (attrs, attr);
}
}
@@ -1769,7 +1769,7 @@ add_child_attrs (GtkTextLayout *layout,
width = 1;
height = 1;
tmp_list = seg->body.child.widgets;
while (tmp_list != NULL)
{
@@ -1786,10 +1786,10 @@ add_child_attrs (GtkTextLayout *layout,
height = req.height;
widget = child;
break;
}
tmp_list = tmp_list->next;
}
@@ -1833,7 +1833,7 @@ add_child_attrs (GtkTextLayout *layout,
* @cursor_at_line_end: whether cursor is at the end of line
*
* Checks whether layout should display block cursor at given position.
* For this layout must be in overwrite mode and text at @insert_iter
* For this layout must be in overwrite mode and text at @insert_iter
* must be editable.
*/
static gboolean
@@ -1916,7 +1916,7 @@ static gboolean
is_shape (PangoLayoutRun *run)
{
GSList *tmp_list = run->item->analysis.extra_attrs;
while (tmp_list)
{
PangoAttribute *attr = tmp_list->data;
@@ -2037,7 +2037,7 @@ add_preedit_attrs (GtkTextLayout *layout,
if (end == G_MAXINT)
end = layout->preedit_len;
if (end == start)
continue;
@@ -2053,13 +2053,13 @@ add_preedit_attrs (GtkTextLayout *layout,
appearance.overline_rgba = gdk_rgba_copy (appearance.overline_rgba);
if (appearance.strikethrough_rgba)
appearance.strikethrough_rgba = gdk_rgba_copy (appearance.strikethrough_rgba);
tmp_list = extra_attrs;
while (tmp_list)
{
PangoAttribute *attr = tmp_list->data;
GdkRGBA rgba;
switch ((guint) attr->klass->type)
{
case PANGO_ATTR_FOREGROUND:
@@ -2108,17 +2108,17 @@ add_preedit_attrs (GtkTextLayout *layout,
default:
break;
}
pango_attribute_destroy (attr);
tmp_list = tmp_list->next;
}
g_slist_free (extra_attrs);
insert_attr = pango_attr_font_desc_new (font_desc);
insert_attr->start_index = start + offset;
insert_attr->end_index = end + offset;
pango_attr_list_insert (attrs, insert_attr);
if (language)
@@ -2126,14 +2126,14 @@ add_preedit_attrs (GtkTextLayout *layout,
insert_attr = pango_attr_language_new (language);
insert_attr->start_index = start + offset;
insert_attr->end_index = end + offset;
pango_attr_list_insert (attrs, insert_attr);
}
add_generic_attrs (layout, &appearance, end - start,
attrs, start + offset,
size_only, TRUE);
if (appearance.fg_rgba)
gdk_rgba_free (appearance.fg_rgba);
if (appearance.bg_rgba)
@@ -2399,7 +2399,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
int bytes = 0;
GtkTextLineSegment *prev_seg = NULL;
while (seg)
{
if (seg->type == &gtk_text_char_type)
@@ -2463,14 +2463,14 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
else if (seg->type == &gtk_text_child_type)
{
saw_widget = TRUE;
add_generic_attrs (layout, &style->appearance,
seg->byte_count,
attrs, layout_byte_offset,
size_only, FALSE);
add_child_attrs (layout, display, style,
seg, attrs, layout_byte_offset);
memcpy (text + layout_byte_offset, seg->body.child.obj->chars,
memcpy (text + layout_byte_offset, gtk_text_child_anchor_get_replacement (seg->body.child.obj),
seg->byte_count);
layout_byte_offset += seg->byte_count;
buffer_byte_offset += seg->byte_count;
@@ -2480,7 +2480,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
/* We don't know this segment type */
g_assert_not_reached ();
}
} /* if (segment was visible) */
else
{
@@ -2509,13 +2509,13 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
seg->type == &gtk_text_left_mark_type)
{
int cursor_offset = 0;
/* At the insertion point, add the preedit string, if any */
if (_gtk_text_btree_mark_is_insert (btree, seg->body.mark.obj))
{
display->insert_index = layout_byte_offset;
if (layout->preedit_len > 0)
{
text_allocated += layout->preedit_len;
@@ -2524,15 +2524,15 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
style = get_style (layout, tags);
add_preedit_attrs (layout, style, attrs, layout_byte_offset, size_only);
release_style (layout, style);
memcpy (text + layout_byte_offset, layout->preedit_string, layout->preedit_len);
layout_byte_offset += layout->preedit_len;
/* DO NOT increment the buffer byte offset for preedit */
cursor_offset = layout->preedit_cursor - layout->preedit_len;
}
}
/* Display visible marks */
@@ -2549,14 +2549,14 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
seg = seg->next;
}
if (!para_values_set)
{
style = get_style (layout, tags);
set_para_values (layout, base_dir, style, display);
release_style (layout, style);
}
/* Pango doesn't want the trailing paragraph delimiters */
{
@@ -2582,7 +2582,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
}
}
}
pango_layout_set_text (display->layout, text, layout_byte_offset);
pango_layout_set_attributes (display->layout, attrs);
@@ -2628,7 +2628,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
break;
}
}
/* Free this if we aren't in a loop */
if (layout->wrap_loop_count == 0)
invalidate_cached_style (layout);
@@ -2642,7 +2642,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
if (saw_widget)
allocate_child_widgets (layout, display);
return g_steal_pointer (&display);
}
@@ -2695,7 +2695,7 @@ line_display_iter_to_index (GtkTextLayout *layout,
g_return_val_if_fail (_gtk_text_iter_get_text_line (iter) == display->line, 0);
index = gtk_text_iter_get_visible_line_index (iter);
if (layout->preedit_len > 0 && display->insert_index >= 0)
{
if (index >= display->insert_index)
@@ -2714,7 +2714,7 @@ line_display_index_to_iter (GtkTextLayout *layout,
{
g_return_if_fail (!_gtk_text_line_is_last (display->line,
_gtk_text_buffer_get_btree (layout->buffer)));
if (layout->preedit_len > 0 && display->insert_index >= 0)
{
if (index >= display->insert_index + layout->preedit_len)
@@ -2729,7 +2729,7 @@ line_display_index_to_iter (GtkTextLayout *layout,
gtk_text_layout_get_iter_at_line (layout, iter, display->line, 0);
gtk_text_iter_set_visible_line_index (iter, index);
if (_gtk_text_iter_get_text_line (iter) != display->line)
{
/* Clamp to end of line - really this clamping should have been done
@@ -2739,7 +2739,7 @@ line_display_index_to_iter (GtkTextLayout *layout,
if (!gtk_text_iter_ends_line (iter))
gtk_text_iter_forward_to_line_end (iter);
}
gtk_text_iter_forward_chars (iter, trailing);
}
@@ -2897,16 +2897,16 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
line = _gtk_text_iter_get_text_line (iter);
display = gtk_text_layout_get_line_display (layout, line, FALSE);
index = line_display_iter_to_index (layout, display, iter);
line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
line, layout);
gtk_text_buffer_get_iter_at_mark (layout->buffer, &insert_iter,
gtk_text_buffer_get_insert (layout->buffer));
if (gtk_text_iter_equal (iter, &insert_iter))
index += layout->preedit_cursor - layout->preedit_len;
pango_layout_get_cursor_pos (display->layout, index,
strong_pos ? &pango_strong_pos : NULL,
weak_pos ? &pango_weak_pos : NULL);
@@ -3038,7 +3038,7 @@ gtk_text_layout_get_iter_location (GtkTextLayout *layout,
GtkTextLineDisplay *display;
int byte_index;
int x_offset;
g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
g_return_if_fail (_gtk_text_iter_get_btree (iter) == _gtk_text_buffer_get_btree (layout->buffer));
g_return_if_fail (rect != NULL);
@@ -3053,9 +3053,9 @@ gtk_text_layout_get_iter_location (GtkTextLayout *layout,
x_offset = display->x_offset * PANGO_SCALE;
byte_index = gtk_text_iter_get_line_index (iter);
pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
rect->x = PANGO_PIXELS (x_offset + pango_rect.x);
rect->y += PANGO_PIXELS (pango_rect.y) + display->top_margin;
rect->width = PANGO_PIXELS (pango_rect.width);
@@ -3106,7 +3106,7 @@ find_display_line_below (GtkTextLayout *layout,
PangoLayoutLine *layout_line = pango_layout_iter_get_line_readonly (layout_iter);
found_byte = layout_line->start_index;
if (line_top >= y)
{
found_line = line;
@@ -3119,7 +3119,7 @@ find_display_line_below (GtkTextLayout *layout,
while (pango_layout_iter_next_line (layout_iter));
pango_layout_iter_free (layout_iter);
line_top += display->bottom_margin;
gtk_text_line_display_unref (display);
@@ -3153,7 +3153,7 @@ find_display_line_above (GtkTextLayout *layout,
if (!line)
{
line = _gtk_text_btree_get_end_iter_line (btree);
line_top = _gtk_text_btree_find_line_top (btree, line, layout);
}
@@ -3165,7 +3165,7 @@ find_display_line_above (GtkTextLayout *layout,
int tmp_top;
layout_iter = pango_layout_get_iter (display->layout);
line_top -= display->top_margin + display->bottom_margin;
pango_layout_iter_get_layout_extents (layout_iter, NULL, &logical_rect);
line_top -= logical_rect.height / PANGO_SCALE;
@@ -3180,7 +3180,7 @@ find_display_line_above (GtkTextLayout *layout,
found_byte = layout_line->start_index;
pango_layout_iter_get_line_yrange (layout_iter, &first_y, &last_y);
tmp_top -= (last_y - first_y) / PANGO_SCALE;
if (tmp_top < y)
@@ -3193,7 +3193,7 @@ find_display_line_above (GtkTextLayout *layout,
while (pango_layout_iter_next_line (layout_iter));
pango_layout_iter_free (layout_iter);
gtk_text_line_display_unref (display);
line = _gtk_text_line_previous (line);
@@ -3272,7 +3272,7 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
PangoLayoutLine *layout_line;
GtkTextIter orig;
gboolean update_byte = FALSE;
g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
@@ -3304,7 +3304,7 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
display = gtk_text_layout_get_line_display (layout, prev_line, FALSE);
update_byte = TRUE;
}
tmp_list = pango_layout_get_lines_readonly (display->layout);
layout_line = tmp_list->data;
@@ -3364,7 +3364,7 @@ gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
}
out:
gtk_text_line_display_unref (display);
return
@@ -3397,7 +3397,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
g_return_val_if_fail (iter != NULL, FALSE);
orig = *iter;
line = _gtk_text_iter_get_text_line (iter);
while (line && !found_after)
@@ -3408,7 +3408,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
if (display->height == 0)
goto next;
if (first)
{
line_byte = line_display_iter_to_index (layout, display, iter);
@@ -3416,7 +3416,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
}
else
line_byte = 0;
tmp_list = pango_layout_get_lines_readonly (display->layout);
while (tmp_list && !found_after)
{
@@ -3430,12 +3430,12 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
}
else if (line_byte < layout_line->start_index + layout_line->length || !tmp_list->next)
found = TRUE;
tmp_list = tmp_list->next;
}
next:
gtk_text_line_display_unref (display);
line = _gtk_text_line_next_excluding_last (line);
@@ -3443,7 +3443,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
if (!found_after)
gtk_text_buffer_get_end_iter (layout->buffer, iter);
return
!gtk_text_iter_equal (iter, &orig) &&
!gtk_text_iter_is_end (iter);
@@ -3467,12 +3467,12 @@ gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
int line_byte;
GSList *tmp_list;
GtkTextIter orig;
g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
orig = *iter;
line = _gtk_text_iter_get_text_line (iter);
display = gtk_text_layout_get_line_display (layout, line, FALSE);
line_byte = line_display_iter_to_index (layout, display, iter);
@@ -3492,13 +3492,13 @@ gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
* are inside a paragraph to avoid going to next line on a
* forced break not at whitespace. Real fix is to keep track
* of whether marks are at leading or trailing edge? */
if (direction > 0 && layout_line->length > 0 &&
!gtk_text_iter_ends_line (iter) &&
if (direction > 0 && layout_line->length > 0 &&
!gtk_text_iter_ends_line (iter) &&
!_gtk_text_btree_char_is_invisible (iter))
gtk_text_iter_backward_char (iter);
break;
}
tmp_list = tmp_list->next;
}
@@ -3525,7 +3525,7 @@ gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
GtkTextLineDisplay *display;
int line_byte;
GSList *tmp_list;
g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
@@ -3545,13 +3545,13 @@ gtk_text_layout_iter_starts_line (GtkTextLayout *layout,
* it
*/
gtk_text_line_display_unref (display);
if (line_byte == layout_line->start_index)
return TRUE;
else
return FALSE;
}
tmp_list = tmp_list->next;
}
@@ -3588,7 +3588,7 @@ gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
GtkTextLineDisplay *display;
int line_byte;
PangoLayoutIter *layout_iter;
g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
g_return_if_fail (iter != NULL);
@@ -3624,7 +3624,7 @@ gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
while (pango_layout_iter_next_line (layout_iter));
pango_layout_iter_free (layout_iter);
gtk_text_line_display_unref (display);
}
@@ -3655,12 +3655,12 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
GtkTextLineDisplay *display = NULL;
GtkTextIter orig;
GtkTextIter lineiter;
g_return_val_if_fail (layout != NULL, FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
orig = *iter;
while (count != 0)
{
GtkTextLine *line = _gtk_text_iter_get_text_line (iter);
@@ -3709,7 +3709,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
extra_back = 1;
}
}
if (new_index < 0 || (new_index == 0 && extra_back))
{
do
@@ -3719,7 +3719,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
goto done;
}
while (totally_invisible_line (layout, line, &lineiter));
gtk_text_line_display_unref (display);
display = gtk_text_layout_get_line_display (layout, line, FALSE);
gtk_text_iter_forward_to_line_end (&lineiter);
@@ -3739,7 +3739,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
display = gtk_text_layout_get_line_display (layout, line, FALSE);
new_index = 0;
}
line_display_index_to_iter (layout, display, iter, new_index, new_trailing);
if (extra_back)
gtk_text_iter_backward_char (iter);
@@ -3748,7 +3748,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
g_clear_pointer (&display, gtk_text_line_display_unref);
done:
return
!gtk_text_iter_equal (iter, &orig) &&
!gtk_text_iter_is_end (iter);
+1 -1
View File
@@ -69,7 +69,7 @@ typedef struct _GtkWindow GtkWindow;
* Refer to each function's documentation for if this value is
* allowed and what it does.
*/
#define GTK_INVALID_LIST_POSITION (0xffffffffU)
#define GTK_INVALID_LIST_POSITION ((guint) 0xffffffff)
G_END_DECLS
+65 -91
View File
@@ -67,13 +67,8 @@ struct _GtkViewport
GtkWidget *child;
GtkAdjustment *hadjustment;
GtkAdjustment *vadjustment;
/* GtkScrollablePolicy needs to be checked when
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
guint vscroll_policy : 1;
GtkAdjustment *adjustment[2];
GtkScrollablePolicy scroll_policy[2];
guint scroll_to_focus : 1;
gulong focus_handler;
@@ -150,66 +145,15 @@ gtk_viewport_buildable_init (GtkBuildableIface *iface)
static void
viewport_set_adjustment_values (GtkViewport *viewport,
GtkOrientation orientation)
GtkOrientation orientation,
int viewport_size,
int child_size)
{
GtkAdjustment *adjustment;
GtkScrollablePolicy scroll_policy;
GtkScrollablePolicy other_scroll_policy;
GtkOrientation other_orientation;
double upper, value;
int viewport_size, other_viewport_size;
int view_width, view_height;
view_width = gtk_widget_get_width (GTK_WIDGET (viewport));
view_height = gtk_widget_get_height (GTK_WIDGET (viewport));
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
adjustment = viewport->hadjustment;
other_orientation = GTK_ORIENTATION_VERTICAL;
viewport_size = view_width;
other_viewport_size = view_height;
scroll_policy = viewport->hscroll_policy;
other_scroll_policy = viewport->vscroll_policy;
}
else /* VERTICAL */
{
adjustment = viewport->vadjustment;
other_orientation = GTK_ORIENTATION_HORIZONTAL;
viewport_size = view_height;
other_viewport_size = view_width;
scroll_policy = viewport->vscroll_policy;
other_scroll_policy = viewport->hscroll_policy;
}
if (viewport->child && gtk_widget_get_visible (viewport->child))
{
int min_size, nat_size;
int scroll_size;
if (other_scroll_policy == GTK_SCROLL_MINIMUM)
gtk_widget_measure (viewport->child, other_orientation, -1,
&scroll_size, NULL, NULL, NULL);
else
gtk_widget_measure (viewport->child, other_orientation, -1,
NULL, &scroll_size, NULL, NULL);
gtk_widget_measure (viewport->child, orientation,
MAX (other_viewport_size, scroll_size),
&min_size, &nat_size, NULL, NULL);
if (scroll_policy == GTK_SCROLL_MINIMUM)
upper = MAX (min_size, viewport_size);
else
upper = MAX (nat_size, viewport_size);
}
else
{
upper = viewport_size;
}
adjustment = viewport->adjustment[orientation];
upper = child_size;
value = gtk_adjustment_get_value (adjustment);
/* We clamp to the left in RTL mode */
@@ -282,7 +226,7 @@ gtk_viewport_get_request_mode (GtkWidget *widget)
#define ADJUSTMENT_POINTER(orientation) \
(((orientation) == GTK_ORIENTATION_HORIZONTAL) ? \
&viewport->hadjustment : &viewport->vadjustment)
&viewport->adjustment[GTK_ORIENTATION_HORIZONTAL] : &viewport->adjustment[GTK_ORIENTATION_VERTICAL])
static void
viewport_disconnect_adjustment (GtkViewport *viewport,
@@ -411,17 +355,17 @@ gtk_viewport_set_property (GObject *object,
viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, g_value_get_object (value));
break;
case PROP_HSCROLL_POLICY:
if (viewport->hscroll_policy != g_value_get_enum (value))
if (viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL] != g_value_get_enum (value))
{
viewport->hscroll_policy = g_value_get_enum (value);
viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL] = g_value_get_enum (value);
gtk_widget_queue_resize (GTK_WIDGET (viewport));
g_object_notify_by_pspec (object, pspec);
}
break;
case PROP_VSCROLL_POLICY:
if (viewport->vscroll_policy != g_value_get_enum (value))
if (viewport->scroll_policy[GTK_ORIENTATION_VERTICAL] != g_value_get_enum (value))
{
viewport->vscroll_policy = g_value_get_enum (value);
viewport->scroll_policy[GTK_ORIENTATION_VERTICAL] = g_value_get_enum (value);
gtk_widget_queue_resize (GTK_WIDGET (viewport));
g_object_notify_by_pspec (object, pspec);
}
@@ -449,16 +393,16 @@ gtk_viewport_get_property (GObject *object,
switch (prop_id)
{
case PROP_HADJUSTMENT:
g_value_set_object (value, viewport->hadjustment);
g_value_set_object (value, viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]);
break;
case PROP_VADJUSTMENT:
g_value_set_object (value, viewport->vadjustment);
g_value_set_object (value, viewport->adjustment[GTK_ORIENTATION_VERTICAL]);
break;
case PROP_HSCROLL_POLICY:
g_value_set_enum (value, viewport->hscroll_policy);
g_value_set_enum (value, viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL]);
break;
case PROP_VSCROLL_POLICY:
g_value_set_enum (value, viewport->vscroll_policy);
g_value_set_enum (value, viewport->scroll_policy[GTK_ORIENTATION_VERTICAL]);
break;
case PROP_SCROLL_TO_FOCUS:
g_value_set_boolean (value, viewport->scroll_to_focus);
@@ -481,8 +425,8 @@ gtk_viewport_init (GtkViewport *viewport)
gtk_widget_set_overflow (widget, GTK_OVERFLOW_HIDDEN);
viewport->hadjustment = NULL;
viewport->vadjustment = NULL;
viewport->adjustment[GTK_ORIENTATION_HORIZONTAL] = NULL;
viewport->adjustment[GTK_ORIENTATION_VERTICAL] = NULL;
viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
@@ -530,8 +474,6 @@ viewport_set_adjustment (GtkViewport *viewport,
*adjustmentp = adjustment;
g_object_ref_sink (adjustment);
viewport_set_adjustment_values (viewport, orientation);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (gtk_viewport_adjustment_value_changed),
viewport);
@@ -546,29 +488,61 @@ gtk_viewport_size_allocate (GtkWidget *widget,
int baseline)
{
GtkViewport *viewport = GTK_VIEWPORT (widget);
GtkAdjustment *hadjustment = viewport->hadjustment;
GtkAdjustment *vadjustment = viewport->vadjustment;
int child_size[2];
g_object_freeze_notify (G_OBJECT (hadjustment));
g_object_freeze_notify (G_OBJECT (vadjustment));
g_object_freeze_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]));
g_object_freeze_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_VERTICAL]));
viewport_set_adjustment_values (viewport, GTK_ORIENTATION_HORIZONTAL);
viewport_set_adjustment_values (viewport, GTK_ORIENTATION_VERTICAL);
child_size[GTK_ORIENTATION_HORIZONTAL] = width;
child_size[GTK_ORIENTATION_VERTICAL] = height;
if (viewport->child && gtk_widget_get_visible (viewport->child))
{
GtkOrientation orientation, opposite;
int min, nat;
if (gtk_widget_get_request_mode (viewport->child) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
orientation = GTK_ORIENTATION_VERTICAL;
else
orientation = GTK_ORIENTATION_HORIZONTAL;
opposite = OPPOSITE_ORIENTATION (orientation);
gtk_widget_measure (viewport->child,
orientation, -1,
&min, &nat,
NULL, NULL);
if (viewport->scroll_policy[orientation] == GTK_SCROLL_MINIMUM)
child_size[orientation] = MAX (child_size[orientation], min);
else
child_size[orientation] = MAX (child_size[orientation], nat);
gtk_widget_measure (viewport->child,
opposite, child_size[orientation],
&min, &nat,
NULL, NULL);
if (viewport->scroll_policy[opposite] == GTK_SCROLL_MINIMUM)
child_size[opposite] = MAX (child_size[opposite], min);
else
child_size[opposite] = MAX (child_size[opposite], nat);
}
viewport_set_adjustment_values (viewport, GTK_ORIENTATION_HORIZONTAL, width, child_size[GTK_ORIENTATION_HORIZONTAL]);
viewport_set_adjustment_values (viewport, GTK_ORIENTATION_VERTICAL, height, child_size[GTK_ORIENTATION_VERTICAL]);
if (viewport->child && gtk_widget_get_visible (viewport->child))
{
GtkAllocation child_allocation;
child_allocation.x = - gtk_adjustment_get_value (hadjustment);
child_allocation.y = - gtk_adjustment_get_value (vadjustment);
child_allocation.width = gtk_adjustment_get_upper (hadjustment);
child_allocation.height = gtk_adjustment_get_upper (vadjustment);
child_allocation.width = child_size[GTK_ORIENTATION_HORIZONTAL];
child_allocation.height = child_size[GTK_ORIENTATION_VERTICAL];
child_allocation.x = - gtk_adjustment_get_value (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]);
child_allocation.y = - gtk_adjustment_get_value (viewport->adjustment[GTK_ORIENTATION_VERTICAL]);
gtk_widget_size_allocate (viewport->child, &child_allocation, -1);
}
g_object_thaw_notify (G_OBJECT (hadjustment));
g_object_thaw_notify (G_OBJECT (vadjustment));
g_object_thaw_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]));
g_object_thaw_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_VERTICAL]));
}
static void
@@ -670,8 +644,8 @@ focus_change_handler (GtkWidget *widget)
rect.origin.y,
&x, &y);
scroll_to_view (viewport->hadjustment, x, rect.size.width);
scroll_to_view (viewport->vadjustment, y, rect.size.height);
scroll_to_view (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL], x, rect.size.width);
scroll_to_view (viewport->adjustment[GTK_ORIENTATION_VERTICAL], y, rect.size.height);
}
static void
@@ -682,7 +656,7 @@ setup_focus_change_handler (GtkViewport *viewport)
root = gtk_widget_get_root (GTK_WIDGET (viewport));
viewport->focus_handler = g_signal_connect_swapped (root, "notify::focus-widget",
G_CALLBACK (focus_change_handler), viewport);
G_CALLBACK (focus_change_handler), viewport);
}
static void
+316
View File
@@ -0,0 +1,316 @@
/*
* Copyright (c) 2021 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include "clipboard.h"
#include "gtkdataviewer.h"
#include "window.h"
#include "gtkbinlayout.h"
#include "gtkbox.h"
#include "gtkdebug.h"
#include "gtklabel.h"
#include "gtklistbox.h"
#include "gtktogglebutton.h"
struct _GtkInspectorClipboard
{
GtkWidget parent;
GdkDisplay *display;
GtkWidget *swin;
GtkWidget *clipboard_formats;
GtkWidget *clipboard_info;
GtkWidget *primary_formats;
GtkWidget *primary_info;
};
typedef struct _GtkInspectorClipboardClass
{
GtkWidgetClass parent_class;
} GtkInspectorClipboardClass;
G_DEFINE_TYPE (GtkInspectorClipboard, gtk_inspector_clipboard, GTK_TYPE_WIDGET)
static void
load_gtype_value (GObject *source,
GAsyncResult *res,
gpointer data)
{
GdkClipboard *clipboard = GDK_CLIPBOARD (source);
GtkDataViewer *viewer = data;
const GValue *value;
GError *error = NULL;
value = gdk_clipboard_read_value_finish (clipboard, res, &error);
if (value == NULL)
gtk_data_viewer_load_error (viewer, error);
else
gtk_data_viewer_load_value (viewer, value);
g_object_unref (viewer);
}
static gboolean
load_gtype (GtkDataViewer *viewer,
GCancellable *cancellable,
gpointer gtype)
{
GdkClipboard *clipboard = g_object_get_data (G_OBJECT (viewer), "clipboard");
gdk_clipboard_read_value_async (clipboard,
GPOINTER_TO_SIZE (gtype),
G_PRIORITY_DEFAULT,
cancellable,
load_gtype_value,
g_object_ref (viewer));
return TRUE;
}
static void
load_mime_type_stream (GObject *source,
GAsyncResult *res,
gpointer data)
{
GdkClipboard *clipboard = GDK_CLIPBOARD (source);
GtkDataViewer *viewer = data;
GInputStream *stream;
GError *error = NULL;
const char *mime_type;
stream = gdk_clipboard_read_finish (clipboard, res, &mime_type, &error);
if (stream == NULL)
gtk_data_viewer_load_error (viewer, error);
else
gtk_data_viewer_load_stream (viewer, stream, mime_type);
g_object_unref (viewer);
}
static gboolean
load_mime_type (GtkDataViewer *viewer,
GCancellable *cancellable,
gpointer mime_type)
{
GdkClipboard *clipboard = g_object_get_data (G_OBJECT (viewer), "clipboard");
gdk_clipboard_read_async (clipboard,
(const char *[2]) { mime_type, NULL },
G_PRIORITY_DEFAULT,
cancellable,
load_mime_type_stream,
g_object_ref (viewer));
return TRUE;
}
static void
add_content_type_row (GtkInspectorClipboard *self,
GtkListBox *list,
const char *type_name,
GdkClipboard *clipboard,
GCallback load_func,
gpointer load_func_data)
{
GtkWidget *row, *vbox, *hbox, *label, *viewer, *button;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40);
gtk_box_append (GTK_BOX (vbox), hbox);
label = gtk_label_new (type_name);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_widget_set_hexpand (label, TRUE);
gtk_box_append (GTK_BOX (hbox), label);
button = gtk_toggle_button_new_with_label (_("Show"));
gtk_widget_set_halign (button, GTK_ALIGN_END);
gtk_widget_set_valign (button, GTK_ALIGN_BASELINE);
gtk_box_append (GTK_BOX (hbox), button);
viewer = gtk_data_viewer_new ();
g_signal_connect (viewer, "load", load_func, load_func_data);
g_object_set_data (G_OBJECT (viewer), "clipboard", clipboard);
g_object_bind_property (G_OBJECT (button), "active",
G_OBJECT (viewer), "visible",
G_BINDING_SYNC_CREATE);
gtk_box_append (GTK_BOX (vbox), viewer);
row = gtk_list_box_row_new ();
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), vbox);
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
gtk_list_box_insert (list, row, -1);
}
static void
init_formats (GtkInspectorClipboard *self,
GtkListBox *list,
GdkClipboard *clipboard)
{
GtkListBoxRow *row;
GdkContentFormats *formats;
const char * const *mime_types;
const GType *gtypes;
gsize i, n;
while ((row = gtk_list_box_get_row_at_index (list, 1)))
gtk_list_box_remove (list, GTK_WIDGET (row));
formats = gdk_clipboard_get_formats (clipboard);
gtypes = gdk_content_formats_get_gtypes (formats, &n);
for (i = 0; i < n; i++)
add_content_type_row (self, list, g_type_name (gtypes[i]), clipboard, G_CALLBACK (load_gtype), GSIZE_TO_POINTER (gtypes[i]));
mime_types = gdk_content_formats_get_mime_types (formats, &n);
for (i = 0; i < n; i++)
add_content_type_row (self, list, mime_types[i], clipboard, G_CALLBACK (load_mime_type), (gpointer) mime_types[i]);
}
static void
init_info (GtkInspectorClipboard *self,
GtkLabel *label,
GdkClipboard *clipboard)
{
GdkContentFormats *formats;
formats = gdk_clipboard_get_formats (clipboard);
if (gdk_content_formats_get_gtypes (formats, NULL) == NULL &&
gdk_content_formats_get_mime_types (formats, NULL) == NULL)
{
gtk_label_set_text (label, C_("clipboard", "empty"));
return;
}
if (gdk_clipboard_is_local (clipboard))
gtk_label_set_text (label, C_("clipboard", "local"));
else
gtk_label_set_text (label, C_("clipboard", "remote"));
}
static void
clipboard_notify (GdkClipboard *clipboard,
GParamSpec *pspec,
GtkInspectorClipboard *self)
{
if (g_str_equal (pspec->name, "formats"))
{
init_formats (self, GTK_LIST_BOX (self->clipboard_formats), clipboard);
}
init_info (self, GTK_LABEL (self->clipboard_info), clipboard);
}
static void
primary_notify (GdkClipboard *clipboard,
GParamSpec *pspec,
GtkInspectorClipboard *self)
{
if (g_str_equal (pspec->name, "formats"))
{
init_formats (self, GTK_LIST_BOX (self->primary_formats), clipboard);
}
g_print ("%s: %s\n", pspec->name, gdk_content_formats_to_string (gdk_clipboard_get_formats (clipboard)));
init_info (self, GTK_LABEL (self->primary_info), clipboard);
}
static void
gtk_inspector_clipboard_unset_display (GtkInspectorClipboard *self)
{
GdkClipboard *clipboard;
if (self->display == NULL)
return;
clipboard = gdk_display_get_clipboard (self->display);
g_signal_handlers_disconnect_by_func (clipboard, clipboard_notify, self);
clipboard = gdk_display_get_primary_clipboard (self->display);
g_signal_handlers_disconnect_by_func (clipboard, primary_notify, self);
}
static void
gtk_inspector_clipboard_init (GtkInspectorClipboard *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
static void
gtk_inspector_clipboard_dispose (GObject *object)
{
GtkInspectorClipboard *self = GTK_INSPECTOR_CLIPBOARD (object);
gtk_inspector_clipboard_unset_display (self);
g_clear_pointer (&self->swin, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_inspector_clipboard_parent_class)->dispose (object);
}
static void
gtk_inspector_clipboard_class_init (GtkInspectorClipboardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = gtk_inspector_clipboard_dispose;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/clipboard.ui");
gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, swin);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, clipboard_formats);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, clipboard_info);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, primary_formats);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, primary_info);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}
void
gtk_inspector_clipboard_set_display (GtkInspectorClipboard *self,
GdkDisplay *display)
{
GdkClipboard *clipboard;
gtk_inspector_clipboard_unset_display (self);
self->display = display;
if (display == NULL)
return;
clipboard = gdk_display_get_clipboard (display);
g_signal_connect (clipboard, "notify", G_CALLBACK (clipboard_notify), self);
init_formats (self, GTK_LIST_BOX (self->clipboard_formats), clipboard);
init_info (self, GTK_LABEL (self->clipboard_info), clipboard);
clipboard = gdk_display_get_primary_clipboard (display);
g_signal_connect (clipboard, "notify", G_CALLBACK (primary_notify), self);
init_formats (self, GTK_LIST_BOX (self->primary_formats), clipboard);
init_info (self, GTK_LABEL (self->primary_info), clipboard);
}
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2021 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _GTK_INSPECTOR_CLIPBOARD_H_
#define _GTK_INSPECTOR_CLIPBOARD_H_
#include <gtk/gtkwidget.h>
#define GTK_TYPE_INSPECTOR_CLIPBOARD (gtk_inspector_clipboard_get_type())
#define GTK_INSPECTOR_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_INSPECTOR_CLIPBOARD, GtkInspectorClipboard))
#define GTK_INSPECTOR_IS_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_INSPECTOR_CLIPBOARD))
typedef struct _GtkInspectorClipboard GtkInspectorClipboard;
G_BEGIN_DECLS
GType gtk_inspector_clipboard_get_type (void);
void gtk_inspector_clipboard_set_display (GtkInspectorClipboard *self,
GdkDisplay *display);
G_END_DECLS
#endif // _GTK_INSPECTOR_CLIPBOARD_H_
+98
View File
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40">
<template class="GtkInspectorClipboard" parent="GtkWidget">
<child>
<object class="GtkScrolledWindow" id="swin">
<property name="hscrollbar-policy">never</property>
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<property name="margin-start">60</property>
<property name="margin-end">60</property>
<property name="margin-top">60</property>
<property name="margin-bottom">60</property>
<property name="spacing">10</property>
<child>
<object class="GtkFrame">
<child>
<object class="GtkListBox" id="clipboard_formats">
<property name="selection-mode">none</property>
<style>
<class name="rich-list"/>
</style>
<child>
<object class="GtkListBoxRow">
<property name="activatable">0</property>
<child>
<object class="GtkBox">
<property name="spacing">40</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Clipboard</property>
<property name="halign">start</property>
<property name="valign">baseline</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="clipboard_info">
<property name="selectable">1</property>
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="ellipsize">end</property>
<property name="hexpand">1</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkFrame">
<child>
<object class="GtkListBox" id="primary_formats">
<property name="selection-mode">none</property>
<style>
<class name="rich-list"/>
</style>
<child>
<object class="GtkListBoxRow">
<property name="activatable">0</property>
<child>
<object class="GtkBox">
<property name="spacing">40</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Primary</property>
<property name="halign">start</property>
<property name="valign">baseline</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="primary_info">
<property name="selectable">1</property>
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="ellipsize">end</property>
<property name="hexpand">1</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>
+412
View File
@@ -0,0 +1,412 @@
/*
* Copyright © 2021 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkdataviewer.h"
#include "gtkbinlayout.h"
#include "gtklabel.h"
#include "gtkpicture.h"
#include "gtkcolorswatchprivate.h"
#include "gtkbox.h"
struct _GtkDataViewer
{
GtkWidget parent_instance;
GtkWidget *contents;
GCancellable *cancellable;
GError *error;
enum {
NOT_LOADED = 0,
LOADING_DONE,
LOADING_EXTERNALLY,
LOADING_INTERNALLY,
LOADING_FAILED
} loading;
};
enum
{
PROP_0,
PROP_LOADING,
N_PROPS
};
enum {
LOAD,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkDataViewer, gtk_data_viewer, GTK_TYPE_WIDGET)
static GParamSpec *properties[N_PROPS] = { NULL, };
static guint signals[LAST_SIGNAL];
static void
gtk_data_viewer_ensure_loaded (GtkDataViewer *self)
{
gboolean started_loading;
if (self->loading != NOT_LOADED)
return;
self->loading = LOADING_EXTERNALLY;
self->cancellable = g_cancellable_new ();
g_signal_emit (self, signals[LOAD], 0, self->cancellable, &started_loading);
if (!started_loading)
{
self->loading = LOADING_FAILED; /* avoid notify::is_loading */
gtk_data_viewer_load_error (self, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Nothing to load"));
}
g_assert (self->loading != NOT_LOADED);
if (gtk_data_viewer_is_loading (self))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
}
static void
gtk_data_viewer_realize (GtkWidget *widget)
{
GtkDataViewer *self = GTK_DATA_VIEWER (widget);
GTK_WIDGET_CLASS (gtk_data_viewer_parent_class)->realize (widget);
gtk_data_viewer_ensure_loaded (self);
}
static void
gtk_data_viewer_unrealize (GtkWidget *widget)
{
GtkDataViewer *self = GTK_DATA_VIEWER (widget);
GTK_WIDGET_CLASS (gtk_data_viewer_parent_class)->unrealize (widget);
gtk_data_viewer_reset (self);
}
static void
gtk_data_viewer_dispose (GObject *object)
{
//GtkDataViewer *self = GTK_DATA_VIEWER (object);
G_OBJECT_CLASS (gtk_data_viewer_parent_class)->dispose (object);
}
static void
gtk_data_viewer_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkDataViewer *self = GTK_DATA_VIEWER (object);
switch (property_id)
{
case PROP_LOADING:
g_value_set_boolean (value, gtk_data_viewer_is_loading (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_data_viewer_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
//GtkDataViewer *self = GTK_DATA_VIEWER (object);
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_data_viewer_class_init (GtkDataViewerClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
widget_class->realize = gtk_data_viewer_realize;
widget_class->unrealize = gtk_data_viewer_unrealize;
gobject_class->dispose = gtk_data_viewer_dispose;
gobject_class->get_property = gtk_data_viewer_get_property;
gobject_class->set_property = gtk_data_viewer_set_property;
properties[PROP_LOADING] =
g_param_spec_boolean ("loading",
"Loading",
"If the widget is currently loading the data to display",
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
signals[LOAD] =
g_signal_new ("load",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL,
NULL,
G_TYPE_BOOLEAN, 1,
G_TYPE_CANCELLABLE);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "frame");
}
static void
gtk_data_viewer_init (GtkDataViewer *self)
{
}
GtkWidget *
gtk_data_viewer_new (void)
{
return g_object_new (GTK_TYPE_DATA_VIEWER, NULL);
}
gboolean
gtk_data_viewer_is_loading (GtkDataViewer *self)
{
g_return_val_if_fail (GTK_IS_DATA_VIEWER (self), FALSE);
return self->loading == LOADING_EXTERNALLY ||
self->loading == LOADING_INTERNALLY;
}
void
gtk_data_viewer_reset (GtkDataViewer *self)
{
gboolean was_loading;
g_return_if_fail (GTK_IS_DATA_VIEWER (self));
g_object_freeze_notify (G_OBJECT (self));
was_loading = gtk_data_viewer_is_loading (self);
g_clear_pointer (&self->contents, gtk_widget_unparent);
g_clear_error (&self->error);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
self->loading = NOT_LOADED;
if (gtk_widget_get_realized (GTK_WIDGET (self)))
gtk_data_viewer_ensure_loaded (self);
if (was_loading != gtk_data_viewer_is_loading (self))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
g_object_thaw_notify (G_OBJECT (self));
}
void
gtk_data_viewer_load_value (GtkDataViewer *self,
const GValue *value)
{
gboolean was_loading;
g_return_if_fail (GTK_IS_DATA_VIEWER (self));
was_loading = gtk_data_viewer_is_loading (self);
self->loading = LOADING_DONE;
g_clear_pointer (&self->contents, gtk_widget_unparent);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_STRING))
{
self->contents = gtk_label_new (g_value_get_string (value));
gtk_label_set_wrap (GTK_LABEL (self->contents), TRUE);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_PAINTABLE))
{
self->contents = gtk_picture_new_for_paintable (g_value_get_object (value));
gtk_widget_set_size_request (self->contents, 256, 256);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_PIXBUF))
{
self->contents = gtk_picture_new_for_pixbuf (g_value_get_object (value));
gtk_widget_set_size_request (self->contents, 256, 256);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_RGBA))
{
const GdkRGBA *color = g_value_get_boxed (value);
self->contents = gtk_color_swatch_new ();
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (self->contents), color);
gtk_widget_set_size_request (self->contents, 48, 32);
gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_FILE))
{
GFile *file = g_value_get_object (value);
self->contents = gtk_label_new (g_file_peek_path (file));
gtk_label_set_ellipsize (GTK_LABEL (self->contents), PANGO_ELLIPSIZE_START);
gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_FILE_LIST))
{
GList *l;
self->contents = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
for (l = g_value_get_boxed (value); l; l = l->next)
{
GFile *file = l->data;
GtkWidget *label = gtk_label_new (g_file_peek_path (file));
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_START);
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (self->contents), label);
}
}
else
{
gtk_data_viewer_load_error (self,
g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"Cannot display objects of type \"%s\"", G_VALUE_TYPE_NAME (value)));
}
if (was_loading)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
}
static void
gtk_data_viewer_load_stream_done (GObject *source,
GAsyncResult *res,
gpointer data)
{
GtkDataViewer *self = data;
GError *error = NULL;
GValue value = G_VALUE_INIT;
if (!gdk_content_deserialize_finish (res, &value, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
gtk_data_viewer_load_error (self, error);
else
g_clear_error (&error);
g_object_unref (self);
return;
}
gtk_data_viewer_load_value (self, &value);
g_object_unref (self);
g_value_unset (&value);
}
void
gtk_data_viewer_load_stream (GtkDataViewer *self,
GInputStream *stream,
const char *mime_type)
{
GdkContentFormats *formats;
const GType *gtypes;
gboolean was_loading;
g_return_if_fail (GTK_IS_DATA_VIEWER (self));
g_return_if_fail (G_IS_INPUT_STREAM (stream));
g_return_if_fail (mime_type != NULL);
was_loading = gtk_data_viewer_is_loading (self);
self->loading = LOADING_INTERNALLY;
if (self->cancellable == NULL)
self->cancellable = g_cancellable_new ();
formats = gdk_content_formats_new (&mime_type, 1);
formats = gdk_content_formats_union_deserialize_gtypes (formats);
gtypes = gdk_content_formats_get_gtypes (formats, NULL);
if (gtypes)
{
gdk_content_deserialize_async (stream,
mime_type,
gtypes[0],
G_PRIORITY_DEFAULT,
self->cancellable,
gtk_data_viewer_load_stream_done,
g_object_ref (self));
if (!was_loading)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
}
else
{
gtk_data_viewer_load_error (self,
g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
"Cannot display data of type \"%s\"", mime_type));
}
gdk_content_formats_unref (formats);
}
void
gtk_data_viewer_load_error (GtkDataViewer *self,
GError *error)
{
gboolean was_loading;
g_return_if_fail (GTK_IS_DATA_VIEWER (self));
was_loading = gtk_data_viewer_is_loading (self);
self->loading = LOADING_FAILED;
g_clear_pointer (&self->contents, gtk_widget_unparent);
g_clear_error (&self->error);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
self->error = error;
self->contents = gtk_label_new (error->message);
gtk_widget_add_css_class (self->contents, "error");
gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_valign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
if (was_loading)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 Benjamin Otte
* Copyright © 2021 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,37 +17,31 @@
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __OTTIE_PATH_VALUE_PRIVATE_H__
#define __OTTIE_PATH_VALUE_PRIVATE_H__
#ifndef __GTK_DATA_VIEWER_H__
#define __GTK_DATA_VIEWER_H__
#include <json-glib/json-glib.h>
#include <gsk/gsk.h>
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
typedef struct _OttiePathValue OttiePathValue;
#define GTK_TYPE_DATA_VIEWER (gtk_data_viewer_get_type ())
struct _OttiePathValue
{
gboolean is_static;
union {
gpointer static_value;
gpointer keyframes;
};
};
G_DECLARE_FINAL_TYPE (GtkDataViewer, gtk_data_viewer, GTK, DATA_VIEWER, GtkWidget)
void ottie_path_value_init (OttiePathValue *self);
void ottie_path_value_clear (OttiePathValue *self);
GtkWidget * gtk_data_viewer_new (void);
GskPath * ottie_path_value_get (OttiePathValue *self,
double timestamp,
gboolean reverse);
gboolean gtk_data_viewer_is_loading (GtkDataViewer *self);
gboolean ottie_path_value_parse (JsonReader *reader,
gsize offset,
gpointer data);
void gtk_data_viewer_reset (GtkDataViewer *self);
void gtk_data_viewer_load_value (GtkDataViewer *self,
const GValue *value);
void gtk_data_viewer_load_stream (GtkDataViewer *self,
GInputStream *stream,
const char *mime_type);
void gtk_data_viewer_load_error (GtkDataViewer *self,
GError *error);
G_END_DECLS
#endif /* __OTTIE_PATH_VALUE_PRIVATE_H__ */
#endif /* __GTK_DATA_VIEWER_H__ */
+2
View File
@@ -27,6 +27,7 @@
#include "a11y.h"
#include "actions.h"
#include "cellrenderergraph.h"
#include "clipboard.h"
#include "controllers.h"
#include "css-editor.h"
#include "css-node-tree.h"
@@ -64,6 +65,7 @@ gtk_inspector_init (void)
g_type_ensure (GTK_TYPE_GRAPH_DATA);
g_type_ensure (GTK_TYPE_INSPECTOR_A11Y);
g_type_ensure (GTK_TYPE_INSPECTOR_ACTIONS);
g_type_ensure (GTK_TYPE_INSPECTOR_CLIPBOARD);
g_type_ensure (GTK_TYPE_INSPECTOR_CONTROLLERS);
g_type_ensure (GTK_TYPE_INSPECTOR_CSS_EDITOR);
g_type_ensure (GTK_TYPE_INSPECTOR_CSS_NODE_TREE);
+2
View File
@@ -5,6 +5,7 @@ inspector_sources = files(
'actions.c',
'baselineoverlay.c',
'cellrenderergraph.c',
'clipboard.c',
'controllers.c',
'css-editor.c',
'css-node-tree.c',
@@ -13,6 +14,7 @@ inspector_sources = files(
'general.c',
'graphdata.c',
'gtktreemodelcssnode.c',
'gtkdataviewer.c',
'highlightoverlay.c',
'init.c',
'inspect-button.c',
+3 -60
View File
@@ -164,12 +164,6 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_ROUNDED_CLIP_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
case GSK_FILL_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1);
case GSK_STROKE_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_stroke_node_get_child (node) }, 1);
case GSK_SHADOW_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
@@ -291,10 +285,6 @@ node_type_name (GskRenderNodeType type)
return "Clip";
case GSK_ROUNDED_CLIP_NODE:
return "Rounded Clip";
case GSK_FILL_NODE:
return "Fill";
case GSK_STROKE_NODE:
return "Stroke";
case GSK_SHADOW_NODE:
return "Shadow";
case GSK_BLEND_NODE:
@@ -334,8 +324,6 @@ node_name (GskRenderNode *node)
case GSK_REPEAT_NODE:
case GSK_CLIP_NODE:
case GSK_ROUNDED_CLIP_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_SHADOW_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
@@ -617,20 +605,6 @@ add_float_row (GtkListStore *store,
g_free (text);
}
static const char *
enum_to_nick (GType type,
int value)
{
GEnumClass *class;
GEnumValue *v;
class = g_type_class_ref (type);
v = g_enum_get_value (class, value);
g_type_class_unref (class);
return v->value_nick;
}
static void
populate_render_node_properties (GtkListStore *store,
GskRenderNode *node)
@@ -893,7 +867,9 @@ populate_render_node_properties (GtkListStore *store,
case GSK_BLEND_NODE:
{
GskBlendMode mode = gsk_blend_node_get_blend_mode (node);
add_text_row (store, "Blendmode", enum_to_nick (GSK_TYPE_BLEND_MODE, mode));
tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode);
add_text_row (store, "Blendmode", tmp);
g_free (tmp);
}
break;
@@ -1126,39 +1102,6 @@ populate_render_node_properties (GtkListStore *store,
}
break;
case GSK_FILL_NODE:
{
GskPath *path = gsk_fill_node_get_path (node);
GskFillRule fill_rule = gsk_fill_node_get_fill_rule (node);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
add_text_row (store, "Fill rule", enum_to_nick (GSK_TYPE_FILL_RULE, fill_rule));
}
break;
case GSK_STROKE_NODE:
{
GskPath *path = gsk_stroke_node_get_path (node);
const GskStroke *stroke = gsk_stroke_node_get_stroke (node);
GskLineCap line_cap = gsk_stroke_get_line_cap (stroke);
GskLineJoin line_join = gsk_stroke_get_line_join (stroke);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
tmp = g_strdup_printf ("%.2f", gsk_stroke_get_line_width (stroke));
add_text_row (store, "Line width", tmp);
g_free (tmp);
add_text_row (store, "Line cap", enum_to_nick (GSK_TYPE_LINE_CAP, line_cap));
add_text_row (store, "Line join", enum_to_nick (GSK_TYPE_LINE_JOIN, line_join));
}
break;
case GSK_CONTAINER_NODE:
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
add_text_row (store, "Children", tmp);

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