Compare commits
5 Commits
path-strok
...
path-tests
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e16c836f60 | ||
|
|
55bfc167e3 | ||
|
|
5516ec2bf0 | ||
|
|
7e7d1987a3 | ||
|
|
304c3055ad |
@@ -62,6 +62,10 @@ struct _GskContourClass
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
float (* get_curvature) (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center);
|
||||
gboolean (* get_closest_point) (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float tolerance,
|
||||
@@ -301,6 +305,15 @@ gsk_rect_contour_get_point (const GskContour *contour,
|
||||
graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f));
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_rect_contour_get_curvature (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_rect_contour_get_closest_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
@@ -564,6 +577,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
|
||||
gsk_rect_contour_init_measure,
|
||||
gsk_rect_contour_free_measure,
|
||||
gsk_rect_contour_get_point,
|
||||
gsk_rect_contour_get_curvature,
|
||||
gsk_rect_contour_get_closest_point,
|
||||
gsk_rect_contour_copy,
|
||||
gsk_rect_contour_add_segment,
|
||||
@@ -765,6 +779,20 @@ gsk_circle_contour_get_point (const GskContour *contour,
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_circle_contour_get_curvature (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (center)
|
||||
*center = self->center;
|
||||
|
||||
return 1 / self->radius;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_circle_contour_get_closest_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
@@ -950,6 +978,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
|
||||
gsk_circle_contour_init_measure,
|
||||
gsk_circle_contour_free_measure,
|
||||
gsk_circle_contour_get_point,
|
||||
gsk_circle_contour_get_curvature,
|
||||
gsk_circle_contour_get_closest_point,
|
||||
gsk_circle_contour_copy,
|
||||
gsk_circle_contour_add_segment,
|
||||
@@ -1286,6 +1315,38 @@ gsk_standard_contour_get_point (const GskContour *contour,
|
||||
gsk_curve_get_tangent (&curve, progress, tangent);
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_standard_contour_get_curvature (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
GskStandardContour *self = (GskStandardContour *) contour;
|
||||
GArray *array = measure_data;
|
||||
guint index;
|
||||
float progress;
|
||||
GskStandardContourMeasure *measure;
|
||||
GskCurve curve;
|
||||
|
||||
if (array->len == 0)
|
||||
{
|
||||
g_assert (distance == 0);
|
||||
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &index))
|
||||
index = array->len - 1;
|
||||
measure = &g_array_index (array, GskStandardContourMeasure, index);
|
||||
progress = (distance - measure->start) / (measure->end - measure->start);
|
||||
progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
|
||||
g_assert (progress >= 0 && progress <= 1);
|
||||
|
||||
gsk_curve_init (&curve, self->ops[measure->op]);
|
||||
|
||||
return gsk_curve_get_curvature (&curve, progress, center);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
@@ -1675,6 +1736,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
gsk_standard_contour_init_measure,
|
||||
gsk_standard_contour_free_measure,
|
||||
gsk_standard_contour_get_point,
|
||||
gsk_standard_contour_get_curvature,
|
||||
gsk_standard_contour_get_closest_point,
|
||||
gsk_standard_contour_copy,
|
||||
gsk_standard_contour_add_segment,
|
||||
@@ -1803,6 +1865,15 @@ gsk_contour_get_point (const GskContour *self,
|
||||
self->klass->get_point (self, measure_data, distance, pos, tangent);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_contour_get_curvature (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
return self->klass->get_curvature (self, measure_data, distance, center);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_contour_get_closest_point (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
|
||||
@@ -74,6 +74,10 @@ void gsk_contour_get_point (const GskContou
|
||||
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,
|
||||
|
||||
@@ -377,6 +377,59 @@ gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @self: a #GskPathMeasure
|
||||
* @distance: distance into the path
|
||||
* @center: (out) (optional) (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
|
||||
|
||||
@@ -65,6 +65,11 @@ void gsk_path_measure_get_point (GskPathMeasure
|
||||
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);
|
||||
|
||||
@@ -111,6 +111,14 @@ gsk_stroke_free (GskStroke *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)
|
||||
|
||||
@@ -80,6 +80,10 @@ void gsk_stroke_set_dash_offset (GskStroke
|
||||
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
|
||||
|
||||
@@ -54,9 +54,6 @@ gsk_stroke_clear (GskStroke *stroke)
|
||||
stroke->n_dash = 0; /* better safe than sorry */
|
||||
}
|
||||
|
||||
void gsk_stroke_to_cairo (const GskStroke *self,
|
||||
cairo_t *cr);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_PRIVATE_H__ */
|
||||
|
||||
2619
tests/curve-editor.c
Normal file
2619
tests/curve-editor.c
Normal file
File diff suppressed because it is too large
Load Diff
38
tests/curve-editor.h
Normal file
38
tests/curve-editor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#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
|
||||
217
tests/curve.c
Normal file
217
tests/curve.c
Normal file
@@ -0,0 +1,217 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "curve-editor.h"
|
||||
|
||||
|
||||
static GskPath *
|
||||
make_circle_path (void)
|
||||
{
|
||||
float w = 200;
|
||||
float h = 200;
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWindow *window;
|
||||
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;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = GTK_WINDOW (gtk_window_new ());
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 250, 250);
|
||||
|
||||
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", 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);
|
||||
|
||||
stroke_toggle = gtk_check_button_new_with_label ("Show outline");
|
||||
gtk_grid_attach (GTK_GRID (grid), stroke_toggle, 1, 5, 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, "color-set", 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);
|
||||
|
||||
reset (NULL, CURVE_EDITOR (demo));
|
||||
|
||||
gtk_window_set_child (window, demo);
|
||||
|
||||
gtk_window_present (window);
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
815
tests/curve2.c
Normal file
815
tests/curve2.c
Normal file
@@ -0,0 +1,815 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
|
||||
|
||||
struct _DemoWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
GskPath *orig_path;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
graphene_point_t point;
|
||||
graphene_point_t point2;
|
||||
graphene_vec2_t tangent;
|
||||
double start, end;
|
||||
float curvature;
|
||||
graphene_point_t center;
|
||||
|
||||
gboolean track;
|
||||
gboolean show_bounding_box;
|
||||
GtkWidget *label;
|
||||
|
||||
gboolean do_stroke;
|
||||
gboolean do_offset;
|
||||
GskPath *stroke_path;
|
||||
GskPathMeasure *stroke_measure;
|
||||
GskStroke *stroke;
|
||||
GskPath *outline_path;
|
||||
GskStroke *outline_stroke;
|
||||
gboolean inside;
|
||||
GskFillRule fill_rule;
|
||||
gboolean do_fill;
|
||||
|
||||
graphene_rect_t bounds;
|
||||
gboolean show_cairo;
|
||||
|
||||
double zoom;
|
||||
};
|
||||
|
||||
struct _DemoWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
DemoWidget *self)
|
||||
{
|
||||
if (self->track)
|
||||
{
|
||||
float distance;
|
||||
char *text;
|
||||
float t;
|
||||
|
||||
gsk_path_measure_get_closest_point_full (self->measure,
|
||||
&GRAPHENE_POINT_INIT (x, y),
|
||||
INFINITY,
|
||||
&distance,
|
||||
&self->point,
|
||||
&t,
|
||||
&self->tangent);
|
||||
|
||||
gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
|
||||
self->curvature = gsk_path_measure_get_curvature (self->measure, t, &self->center);
|
||||
|
||||
text = g_strdup_printf ("%.1f", distance);
|
||||
gtk_label_set_label (GTK_LABEL (self->label), text);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
if (self->do_stroke && self->do_fill)
|
||||
{
|
||||
gboolean inside = TRUE;
|
||||
|
||||
inside = gsk_path_measure_in_fill (self->stroke_measure, &GRAPHENE_POINT_INIT (x, y), self->fill_rule);
|
||||
|
||||
if (self->inside != inside)
|
||||
{
|
||||
self->inside = inside;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_init (DemoWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
self->start = 0;
|
||||
self->end = 1;
|
||||
|
||||
self->label = gtk_label_new ("");
|
||||
gtk_widget_set_parent (self->label, GTK_WIDGET (self));
|
||||
gtk_widget_set_halign (self->label, GTK_ALIGN_END);
|
||||
gtk_widget_set_valign (self->label, GTK_ALIGN_START);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (motion), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
int width, height;
|
||||
GskStroke *stroke;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
graphene_rect_t bounds;
|
||||
|
||||
if (!self->path)
|
||||
return;
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
gtk_snapshot_scale (snapshot, self->zoom, self->zoom);
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_width (widget);
|
||||
|
||||
if (self->do_stroke)
|
||||
{
|
||||
if (self->show_cairo)
|
||||
{
|
||||
cairo_t *cr;
|
||||
|
||||
graphene_rect_init (&bounds, 0, 0, width, height);
|
||||
cr = gtk_snapshot_append_cairo (snapshot, &bounds);
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
gsk_stroke_to_cairo (self->stroke, cr);
|
||||
cairo_set_source_rgba (cr, 0, 0, 1, 0.2);
|
||||
cairo_stroke (cr);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
if (self->do_fill && self->inside)
|
||||
{
|
||||
gtk_snapshot_push_fill (snapshot, self->stroke_path, self->fill_rule);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 1, 0, 1, 0.3},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
gtk_snapshot_push_stroke (snapshot, self->outline_path, self->outline_stroke);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 0, 0, 0, 0.2},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
stroke = gsk_stroke_new (1);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 0, 0, 0, 0.3},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
else if (self->do_offset)
|
||||
{
|
||||
for (int i = -3; i < 4; i++)
|
||||
{
|
||||
path = gsk_path_offset (self->path,
|
||||
5 * i,
|
||||
gsk_stroke_get_line_join (self->stroke),
|
||||
gsk_stroke_get_miter_limit (self->stroke));
|
||||
|
||||
gtk_snapshot_push_stroke (snapshot, path, self->outline_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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, self->outline_stroke);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 0, 0, 0, 1},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
if (self->show_bounding_box)
|
||||
{
|
||||
if (gsk_path_get_bounds (self->do_stroke ? self->outline_path : self->path, &bounds))
|
||||
{
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_add_rect (builder, &bounds);
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
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, 0.5},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->track)
|
||||
{
|
||||
graphene_point_t p;
|
||||
|
||||
p.x = self->point.x + graphene_vec2_get_x (&self->tangent) * 40;
|
||||
p.y = self->point.y + graphene_vec2_get_y (&self->tangent) * 40;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, self->point.x, self->point.y);
|
||||
gsk_path_builder_line_to (builder, p.x, p.y);
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
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);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_add_circle (builder, &self->point, 5);
|
||||
gsk_path_builder_add_circle (builder, &p, 2.5);
|
||||
gsk_path_builder_add_circle (builder, &self->point2, 5);
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, path, 0);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 1, 0, 0, 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);
|
||||
|
||||
if (self->curvature != 0)
|
||||
{
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &self->center, fabs (1/self->curvature));
|
||||
gsk_path_builder_add_circle (builder, &self->center, 3);
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 1, 0, 1, 1},
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height ));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
gtk_widget_snapshot_child (widget, self->label, snapshot);
|
||||
}
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
float size;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
size = self->zoom * (self->bounds.origin.x + self->bounds.size.width);
|
||||
else
|
||||
size = self->zoom * (self->bounds.origin.y + self->bounds.size.height);
|
||||
|
||||
*minimum = *natural = (int)size;
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
GtkRequisition min, nat;
|
||||
|
||||
gtk_widget_get_preferred_size (self->label, &min, &nat);
|
||||
|
||||
gtk_widget_size_allocate (self->label,
|
||||
&(GtkAllocation) { width - nat.width, 0, nat.width, nat.height},
|
||||
-1);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_dispose (GObject *object)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
g_clear_pointer (&self->stroke_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
|
||||
g_clear_pointer (&self->stroke, gsk_stroke_free);
|
||||
g_clear_pointer (&self->outline_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->outline_stroke, gsk_stroke_free);
|
||||
g_clear_pointer (&self->label, gtk_widget_unparent);
|
||||
|
||||
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_class_init (DemoWidgetClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = demo_widget_dispose;
|
||||
|
||||
widget_class->snapshot = demo_widget_snapshot;
|
||||
widget_class->measure = demo_widget_measure;
|
||||
widget_class->size_allocate = demo_widget_size_allocate;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
demo_widget_new (void)
|
||||
{
|
||||
return g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
update_outline_path (DemoWidget *self)
|
||||
{
|
||||
if (self->stroke_path)
|
||||
{
|
||||
g_clear_pointer (&self->outline_path, gsk_path_unref);
|
||||
self->outline_path = gsk_path_ref (self->stroke_path);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_stroke_path (DemoWidget *self)
|
||||
{
|
||||
g_clear_pointer (&self->stroke_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
|
||||
|
||||
if (self->do_stroke)
|
||||
{
|
||||
self->stroke_path = gsk_path_stroke (self->path, self->stroke);
|
||||
self->stroke_measure = gsk_path_measure_new (self->stroke_path);
|
||||
update_outline_path (self);
|
||||
}
|
||||
|
||||
gsk_path_get_bounds (self->orig_path, &self->bounds);
|
||||
|
||||
if (self->stroke_path)
|
||||
{
|
||||
graphene_rect_t b;
|
||||
gsk_path_get_bounds (self->stroke_path, &b);
|
||||
graphene_rect_union (&b, &self->bounds, &self->bounds);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
update_path (DemoWidget *self)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
|
||||
if (self->start > 0 || self->end < 1)
|
||||
{
|
||||
GskPathMeasure *measure;
|
||||
GskPathBuilder *builder;
|
||||
float length;
|
||||
|
||||
measure = gsk_path_measure_new (self->orig_path);
|
||||
length = gsk_path_measure_get_length (measure);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, measure, self->start * length, self->end * length);
|
||||
self->path = gsk_path_builder_free_to_path (builder);
|
||||
gsk_path_measure_unref (measure);
|
||||
}
|
||||
else
|
||||
self->path = gsk_path_ref (self->orig_path);
|
||||
|
||||
self->measure = gsk_path_measure_new (self->path);
|
||||
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_set_path (DemoWidget *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->orig_path, gsk_path_unref);
|
||||
self->orig_path = gsk_path_ref (path);
|
||||
|
||||
update_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
activate (GtkEntry *entry,
|
||||
DemoWidget *demo)
|
||||
{
|
||||
GskPath *path;
|
||||
|
||||
path = gsk_path_parse (gtk_editable_get_text (GTK_EDITABLE (entry)));
|
||||
if (path)
|
||||
{
|
||||
demo_widget_set_path (demo, path);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_demo (DemoWidget *demo,
|
||||
GtkEditable *editable)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
char *string;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (150, 150), 100);
|
||||
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (100, 100, 100, 100));
|
||||
gsk_path_builder_move_to (builder, 300, 150);
|
||||
gsk_path_builder_curve_to (builder, 300, 50, 400, 50, 400, 150);
|
||||
gsk_path_builder_curve_to (builder, 400, 250, 500, 250, 500, 150);
|
||||
gsk_path_builder_line_to (builder, 600, 150);
|
||||
gsk_path_builder_line_to (builder, 530, 190);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
demo_widget_set_path (demo, path);
|
||||
|
||||
string = gsk_path_to_string (path);
|
||||
gtk_editable_set_text (editable, string);
|
||||
g_free (string);
|
||||
gsk_path_unref (path);
|
||||
|
||||
demo->stroke = gsk_stroke_new (20);
|
||||
demo->outline_stroke = gsk_stroke_new (1);
|
||||
|
||||
demo->zoom = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
zoom_changed (GtkRange *range,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->zoom = gtk_range_get_value (range);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
track_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->track = gtk_check_button_get_active (button);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
bb_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->show_bounding_box = gtk_check_button_get_active (button);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->show_cairo = gtk_check_button_get_active (button);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
stroke_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->do_stroke = gtk_check_button_get_active (button);
|
||||
update_stroke_path (self);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
offset_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->do_offset = gtk_check_button_get_active (button);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
fill_toggled (GtkCheckButton *button,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->do_fill = gtk_check_button_get_active (button);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static GtkWidget *start_scale;
|
||||
static GtkWidget *end_scale;
|
||||
|
||||
static void
|
||||
range_changed (GtkRange *range,
|
||||
DemoWidget *self)
|
||||
{
|
||||
double start, end;
|
||||
|
||||
if (range == GTK_RANGE (start_scale))
|
||||
{
|
||||
start = gtk_range_get_value (range);
|
||||
end = MAX (start, gtk_range_get_value (GTK_RANGE (end_scale)));
|
||||
gtk_range_set_value (GTK_RANGE (end_scale), end);
|
||||
}
|
||||
else
|
||||
{
|
||||
end = gtk_range_get_value (range);
|
||||
start = MIN (end, gtk_range_get_value (GTK_RANGE (start_scale)));
|
||||
gtk_range_set_value (GTK_RANGE (start_scale), start);
|
||||
}
|
||||
|
||||
self->start = start;
|
||||
self->end = end;
|
||||
|
||||
update_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
fill_rule_changed (GtkDropDown *combo,
|
||||
GParamSpec *pspec,
|
||||
DemoWidget *self)
|
||||
{
|
||||
self->fill_rule = (GskFillRule)gtk_drop_down_get_selected (combo);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
cap_changed (GtkDropDown *combo,
|
||||
GParamSpec *pspec,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_line_cap (self->stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
join_changed (GtkDropDown *combo,
|
||||
GParamSpec *pspec,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_line_join (self->stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
limit_changed (GtkSpinButton *spin,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_miter_limit (self->stroke, gtk_spin_button_get_value (spin));
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
stroke_width_changed (GtkSpinButton *spin,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_line_width (self->stroke, gtk_spin_button_get_value (spin));
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
line_width_changed (GtkSpinButton *spin,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_line_width (self->outline_stroke, gtk_spin_button_get_value (spin));
|
||||
update_outline_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
dash_changed (GtkEntry *entry,
|
||||
DemoWidget *self)
|
||||
{
|
||||
const char *text;
|
||||
char **s;
|
||||
float dash[20];
|
||||
int i;
|
||||
|
||||
text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
||||
s = g_strsplit (text, " ", -1);
|
||||
for (i = 0; s[i]; i++)
|
||||
{
|
||||
char *end;
|
||||
float f;
|
||||
|
||||
f = g_strtod (s[i], &end);
|
||||
if (*end != '\0')
|
||||
return;
|
||||
|
||||
dash[i] = f;
|
||||
}
|
||||
|
||||
g_strfreev (s);
|
||||
|
||||
gsk_stroke_set_dash (self->stroke, dash, i);
|
||||
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
dash_offset_changed (GtkSpinButton *spin,
|
||||
DemoWidget *self)
|
||||
{
|
||||
gsk_stroke_set_dash_offset (self->stroke, gtk_spin_button_get_value (spin));
|
||||
update_stroke_path (self);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *box, *demo, *entry;
|
||||
GtkWidget *popover, *button, *grid;
|
||||
GtkWidget *header, *toggle, *toggle2, *toggle3;
|
||||
GtkWidget *combo, *spin, *sw, *hbox;
|
||||
GtkWidget *zoom_scale;
|
||||
int row;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_window_set_child (GTK_WINDOW (window), box);
|
||||
|
||||
demo = demo_widget_new ();
|
||||
gtk_widget_set_hexpand (demo, TRUE);
|
||||
gtk_widget_set_vexpand (demo, TRUE);
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_box_append (GTK_BOX (box), sw);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), demo);
|
||||
|
||||
header = gtk_header_bar_new ();
|
||||
button = gtk_menu_button_new ();
|
||||
gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (button), "emblem-system-symbolic");
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
|
||||
|
||||
zoom_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 20, 0.25);
|
||||
gtk_range_set_value (GTK_RANGE (zoom_scale), 1);
|
||||
g_signal_connect (zoom_scale, "value-changed", G_CALLBACK (zoom_changed), demo);
|
||||
gtk_widget_set_size_request (zoom_scale, 150, -1);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), zoom_scale);
|
||||
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
|
||||
popover = gtk_popover_new ();
|
||||
gtk_menu_button_set_popover (GTK_MENU_BUTTON (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);
|
||||
|
||||
row = 0;
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
toggle = gtk_check_button_new_with_label ("Plain");
|
||||
gtk_check_button_set_active (GTK_CHECK_BUTTON (toggle), TRUE);
|
||||
gtk_box_append (GTK_BOX (hbox), toggle);
|
||||
gtk_widget_set_hexpand (toggle, TRUE);
|
||||
toggle2 = gtk_check_button_new_with_label ("Stroke");
|
||||
gtk_widget_set_hexpand (toggle2, TRUE);
|
||||
gtk_box_append (GTK_BOX (hbox), toggle2);
|
||||
gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle2), GTK_CHECK_BUTTON (toggle));
|
||||
g_signal_connect (toggle2, "toggled", G_CALLBACK (stroke_toggled), demo);
|
||||
toggle3 = gtk_check_button_new_with_label ("Offset");
|
||||
gtk_widget_set_hexpand (toggle3, TRUE);
|
||||
gtk_box_append (GTK_BOX (hbox), toggle3);
|
||||
gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle3), GTK_CHECK_BUTTON (toggle));
|
||||
g_signal_connect (toggle3, "toggled", G_CALLBACK (offset_toggled), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), hbox, 0, row++, 2, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Fill rule"), 0, row, 1, 1);
|
||||
|
||||
combo = gtk_drop_down_new_from_strings ((const char *[]){"Winding", "Even-Odd", NULL });
|
||||
g_signal_connect (combo, "notify::selected", G_CALLBACK (fill_rule_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, row, 1, 1);
|
||||
combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
|
||||
g_signal_connect (combo, "notify::selected", G_CALLBACK (cap_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, row, 1, 1);
|
||||
combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs", NULL});
|
||||
g_signal_connect (combo, "notify::selected", G_CALLBACK (join_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, row, 1, 1);
|
||||
spin = gtk_spin_button_new_with_range (0, 10, 1);
|
||||
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 4);
|
||||
g_signal_connect (spin, "value-changed", G_CALLBACK (limit_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Stroke width:"), 0, row, 1, 1);
|
||||
spin = gtk_spin_button_new_with_range (1, 40, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 20);
|
||||
g_signal_connect (spin, "value-changed", G_CALLBACK (stroke_width_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, row, 1, 1);
|
||||
spin = gtk_spin_button_new_with_range (1, 20, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 1);
|
||||
g_signal_connect (spin, "value-changed", G_CALLBACK (line_width_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash pattern:"), 0, row, 1, 1);
|
||||
entry = gtk_entry_new ();
|
||||
g_signal_connect (entry, "changed", G_CALLBACK (dash_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), entry, 1, row++, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash offset:"), 0, row, 1, 1);
|
||||
spin = gtk_spin_button_new_with_range (-1000, 1000, 10);
|
||||
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
|
||||
g_signal_connect (spin, "value-changed", G_CALLBACK (dash_offset_changed), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
|
||||
|
||||
toggle = gtk_check_button_new_with_label ("Show closest point");
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (track_toggled), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
|
||||
|
||||
toggle = gtk_check_button_new_with_label ("Show bounding box");
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (bb_toggled), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
|
||||
|
||||
toggle = gtk_check_button_new_with_label ("Highlight on hover");
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (fill_toggled), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
|
||||
|
||||
toggle = gtk_check_button_new_with_label ("Compare cairo stroke");
|
||||
g_signal_connect (toggle, "toggled", G_CALLBACK (cairo_toggled), demo);
|
||||
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
g_signal_connect (entry, "activate", G_CALLBACK (activate), demo);
|
||||
gtk_box_append (GTK_BOX (box), entry);
|
||||
|
||||
start_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.01);
|
||||
g_signal_connect (start_scale, "value-changed", G_CALLBACK (range_changed), demo);
|
||||
gtk_box_append (GTK_BOX (box), start_scale);
|
||||
|
||||
end_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.01);
|
||||
gtk_range_set_value (GTK_RANGE (end_scale), 1);
|
||||
g_signal_connect (end_scale, "value-changed", G_CALLBACK (range_changed), demo);
|
||||
gtk_box_append (GTK_BOX (box), end_scale);
|
||||
|
||||
init_demo (DEMO_WIDGET (demo), GTK_EDITABLE (entry));
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ gtk_tests = [
|
||||
['blur-performance', ['../gsk/gskcairoblur.c']],
|
||||
['motion-compression'],
|
||||
['overlayscroll'],
|
||||
['curve', ['curve.c', 'curve-editor.c']],
|
||||
['curve2'],
|
||||
['testupload'],
|
||||
['testtransform'],
|
||||
['testdropdown'],
|
||||
|
||||
Reference in New Issue
Block a user