mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 12:32:05 +00:00
Merge branch 'cookbook-layouts-bind-constraint'
* cookbook-layouts-bind-constraint: cookbook: Add recipe about sync'ing actor sizes cookbook: Example using allocation-changed to sync actor size cookbook: Simple example to demonstrate bind constraint cookbook: Example of using a bind constraint for an overlay
This commit is contained in:
commit
699e8bbed2
@ -41,6 +41,7 @@ IMAGE_FILES = \
|
||||
images/textures-sub-texture.png \
|
||||
images/layouts-stacking-diff-actor-sizes.png \
|
||||
images/events-pointer-motion-stacking.png \
|
||||
images/layouts-bind-constraint-stage.png \
|
||||
$(NULL)
|
||||
VIDEO_FILES = \
|
||||
videos/animations-fading-out.ogv \
|
||||
|
@ -10,6 +10,9 @@ noinst_PROGRAMS = \
|
||||
textures-reflection \
|
||||
textures-split-go \
|
||||
textures-sub-texture \
|
||||
layouts-bind-constraint-allocation \
|
||||
layouts-bind-constraint-overlay \
|
||||
layouts-bind-constraint-stage \
|
||||
layouts-stacking \
|
||||
layouts-stacking-diff-sized-actors \
|
||||
events-mouse-scroll \
|
||||
@ -52,6 +55,9 @@ text_shadow_SOURCES = text-shadow.c
|
||||
textures_reflection_SOURCES = textures-reflection.c
|
||||
textures_split_go_SOURCES = textures-split-go.c
|
||||
textures_sub_texture_SOURCES = textures-sub-texture.c
|
||||
layouts_bind_constraint_allocation_SOURCES = layouts-bind-constraint-allocation.c
|
||||
layouts_bind_constraint_overlay_SOURCES = layouts-bind-constraint-overlay.c
|
||||
layouts_bind_constraint_stage_SOURCES = layouts-bind-constraint-stage.c
|
||||
layouts_stacking_SOURCES = layouts-stacking.c
|
||||
layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors.c
|
||||
events_mouse_scroll_SOURCES = events-mouse-scroll.c
|
||||
|
70
doc/cookbook/examples/layouts-bind-constraint-allocation.c
Normal file
70
doc/cookbook/examples/layouts-bind-constraint-allocation.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include <stdlib.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define OVERLAY_FACTOR 1.1
|
||||
|
||||
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||
static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff };
|
||||
static const ClutterColor blue = { 0x00, 0x00, 0xff, 0x66 };
|
||||
|
||||
void
|
||||
allocation_changed_cb (ClutterActor *actor,
|
||||
const ClutterActorBox *allocation,
|
||||
ClutterAllocationFlags flags,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActor *overlay = CLUTTER_ACTOR (user_data);
|
||||
|
||||
gfloat width, height, x, y;
|
||||
clutter_actor_box_get_size (allocation, &width, &height);
|
||||
clutter_actor_box_get_origin (allocation, &x, &y);
|
||||
|
||||
clutter_actor_set_size (overlay,
|
||||
width * OVERLAY_FACTOR,
|
||||
height * OVERLAY_FACTOR);
|
||||
|
||||
clutter_actor_set_position (overlay,
|
||||
x - ((OVERLAY_FACTOR - 1) * width * 0.5),
|
||||
y - ((OVERLAY_FACTOR - 1) * width * 0.5));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
ClutterActor *stage;
|
||||
ClutterActor *actor;
|
||||
ClutterActor *overlay;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_get_default ();
|
||||
clutter_actor_set_size (stage, 400, 400);
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
|
||||
actor = clutter_rectangle_new_with_color (&red);
|
||||
clutter_actor_set_size (actor, 100, 100);
|
||||
clutter_actor_set_position (actor, 150, 150);
|
||||
|
||||
overlay = clutter_rectangle_new_with_color (&blue);
|
||||
|
||||
g_signal_connect (actor,
|
||||
"allocation-changed",
|
||||
G_CALLBACK (allocation_changed_cb),
|
||||
overlay);
|
||||
|
||||
clutter_container_add (CLUTTER_CONTAINER (stage), actor, overlay, NULL);
|
||||
|
||||
clutter_actor_animate (actor, CLUTTER_LINEAR, 2000,
|
||||
"width", 300.0,
|
||||
"height", 300.0,
|
||||
"x", 50.0,
|
||||
"y", 50.0,
|
||||
NULL);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
135
doc/cookbook/examples/layouts-bind-constraint-overlay.c
Normal file
135
doc/cookbook/examples/layouts-bind-constraint-overlay.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <stdlib.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define STAGE_SIDE 400
|
||||
#define RECTANGLE_SIDE STAGE_SIDE * 0.5
|
||||
#define TEXTURE_SIZE_MAX STAGE_SIDE * 0.9
|
||||
#define TEXTURE_SIZE_MIN STAGE_SIDE * 0.1
|
||||
#define TEXTURE_SIZE_STEP 0.2
|
||||
#define OVERLAY_OPACITY_OFF 0
|
||||
#define OVERLAY_OPACITY_ON 100
|
||||
|
||||
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||
static const ClutterColor overlay_color = { 0xaa, 0x99, 0x00, 0xff };
|
||||
|
||||
/* change the texture size with +/- */
|
||||
static gboolean
|
||||
key_press_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActor *texture;
|
||||
gfloat texture_width, texture_height;
|
||||
guint key_pressed;
|
||||
|
||||
texture = CLUTTER_ACTOR (user_data);
|
||||
clutter_actor_get_size (texture, &texture_width, &texture_height);
|
||||
|
||||
key_pressed = clutter_event_get_key_symbol (event);
|
||||
|
||||
if (key_pressed == CLUTTER_KEY_plus)
|
||||
{
|
||||
texture_width *= 1.0 + TEXTURE_SIZE_STEP;
|
||||
texture_height *= 1.0 + TEXTURE_SIZE_STEP;
|
||||
}
|
||||
else if (key_pressed == CLUTTER_KEY_minus)
|
||||
{
|
||||
texture_width *= 1.0 - TEXTURE_SIZE_STEP;
|
||||
texture_height *= 1.0 - TEXTURE_SIZE_STEP;
|
||||
}
|
||||
|
||||
if (texture_width <= TEXTURE_SIZE_MAX && texture_width >= TEXTURE_SIZE_MIN)
|
||||
clutter_actor_animate (texture, CLUTTER_EASE_OUT_CUBIC, 500,
|
||||
"width", texture_width,
|
||||
"height", texture_height,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* turn overlay opacity on/off */
|
||||
static void
|
||||
click_cb (ClutterClickAction *action,
|
||||
ClutterActor *actor,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActor *overlay = CLUTTER_ACTOR (user_data);
|
||||
guint8 opacity = clutter_actor_get_opacity (overlay);
|
||||
|
||||
if (opacity < OVERLAY_OPACITY_ON)
|
||||
opacity = OVERLAY_OPACITY_ON;
|
||||
else
|
||||
opacity = OVERLAY_OPACITY_OFF;
|
||||
|
||||
clutter_actor_set_opacity (overlay, opacity);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
ClutterActor *stage;
|
||||
ClutterActor *texture;
|
||||
ClutterActor *overlay;
|
||||
ClutterAction *click;
|
||||
GError *error = NULL;
|
||||
|
||||
gchar *filename = TESTS_DATA_DIR "/redhand.png";
|
||||
|
||||
if (argc > 1)
|
||||
filename = argv[1];
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_new ();
|
||||
clutter_actor_set_size (stage, STAGE_SIDE, STAGE_SIDE);
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
|
||||
texture = clutter_texture_new ();
|
||||
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
|
||||
clutter_actor_set_reactive (texture, TRUE);
|
||||
clutter_actor_set_size (texture, RECTANGLE_SIDE, RECTANGLE_SIDE);
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5));
|
||||
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||
filename,
|
||||
&error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning ("Error loading %s\n%s", filename, error->message);
|
||||
g_error_free (error);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* overlay is 10px wider and taller than the texture, and centered on it;
|
||||
* initially, it is transparent; but it is made semi-opaque when the
|
||||
* texture is clicked
|
||||
*/
|
||||
overlay = clutter_rectangle_new_with_color (&overlay_color);
|
||||
clutter_actor_set_opacity (overlay, OVERLAY_OPACITY_OFF);
|
||||
clutter_actor_add_constraint (overlay, clutter_bind_constraint_new (texture, CLUTTER_BIND_WIDTH, 10));
|
||||
clutter_actor_add_constraint (overlay, clutter_bind_constraint_new (texture, CLUTTER_BIND_HEIGHT, 10));
|
||||
clutter_actor_add_constraint (overlay, clutter_align_constraint_new (texture, CLUTTER_ALIGN_X_AXIS, 0.5));
|
||||
clutter_actor_add_constraint (overlay, clutter_align_constraint_new (texture, CLUTTER_ALIGN_Y_AXIS, 0.5));
|
||||
|
||||
click = clutter_click_action_new ();
|
||||
clutter_actor_add_action (texture, click);
|
||||
|
||||
clutter_container_add (CLUTTER_CONTAINER (stage), texture, overlay, NULL);
|
||||
clutter_actor_raise_top (overlay);
|
||||
|
||||
g_signal_connect (click, "clicked", G_CALLBACK (click_cb), overlay);
|
||||
|
||||
g_signal_connect (stage,
|
||||
"key-press-event",
|
||||
G_CALLBACK (key_press_cb),
|
||||
texture);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
55
doc/cookbook/examples/layouts-bind-constraint-stage.c
Normal file
55
doc/cookbook/examples/layouts-bind-constraint-stage.c
Normal file
@ -0,0 +1,55 @@
|
||||
#include <stdlib.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||
static const ClutterColor rectangle_color = { 0xaa, 0x99, 0x00, 0xff };
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
/* the stage is the "source" for constraints on the texture */
|
||||
ClutterActor *stage;
|
||||
|
||||
/* the "target" actor which will be bound by the constraints */
|
||||
ClutterActor *texture;
|
||||
|
||||
ClutterConstraint *width_binding;
|
||||
ClutterConstraint *height_binding;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_new ();
|
||||
clutter_actor_set_size (stage, 400, 400);
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||
|
||||
/* make the stage resizable */
|
||||
clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
|
||||
|
||||
texture = clutter_texture_new ();
|
||||
clutter_actor_set_opacity (texture, 50);
|
||||
clutter_texture_set_repeat (CLUTTER_TEXTURE (texture), TRUE, TRUE);
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture), "smiley.png", NULL);
|
||||
|
||||
/* the texture's width will be 100px less than the stage's */
|
||||
width_binding = clutter_bind_constraint_new (stage, CLUTTER_BIND_WIDTH, -100);
|
||||
|
||||
/* the texture's height will be 100px less than the stage's */
|
||||
height_binding = clutter_bind_constraint_new (stage, CLUTTER_BIND_HEIGHT, -100);
|
||||
|
||||
/* add the constraints to the texture */
|
||||
clutter_actor_add_constraint (texture, width_binding);
|
||||
clutter_actor_add_constraint (texture, height_binding);
|
||||
|
||||
/* add some alignment constraints */
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
|
||||
clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5));
|
||||
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
BIN
doc/cookbook/images/layouts-bind-constraint-stage.png
Normal file
BIN
doc/cookbook/images/layouts-bind-constraint-stage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -587,4 +587,224 @@ clutter_actor_raise_top (text);
|
||||
|
||||
</section>
|
||||
|
||||
<section id="layouts-bind-constraint">
|
||||
<title>Binding the size of one actor to the size of another</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want one actor (the "target") to automatically change
|
||||
its width or height (or both) when the size of another
|
||||
actor (the "source") changes.</para>
|
||||
|
||||
<para>Example use cases:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Making an actor adjust itself to the size of the stage
|
||||
(particularly when the stage is resizable).</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Putting one actor on top of another and keeping their
|
||||
sizes in sync.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Create a <type>ClutterBindConstraint</type> bound to the
|
||||
width and/or height of one actor (the "source"). Add that constraint
|
||||
to an actor (the "target") whose size should follow the
|
||||
size of the source.</para>
|
||||
|
||||
<para>This short example shows how to create and add a constraint;
|
||||
<varname>source</varname> and <varname>target</varname> can
|
||||
be any two <type>ClutterActors</type>:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<emphasis>ClutterConstraint *width_constraint;</emphasis>
|
||||
|
||||
/* create a constraint which binds a target actor's width to 100px less than
|
||||
* the width of the source actor (use CLUTTER_BIND_HEIGHT to create a
|
||||
* constraint based on an actor's height)
|
||||
*
|
||||
* the third argument is a positive or negative offset from the actor's
|
||||
* dimension, in pixels; this is added to the height or width of the source
|
||||
* actor before the constraint is applied to the target actor
|
||||
*/
|
||||
<emphasis>width_constraint = clutter_bind_constraint_new (source, CLUTTER_BIND_WIDTH, -100);</emphasis>
|
||||
|
||||
/* add the constraint to an actor */
|
||||
<emphasis>clutter_actor_add_constraint (target, width_constraint);</emphasis>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Below is a full example, showing how to incorporate a
|
||||
constraint into a Clutter application.</para>
|
||||
|
||||
<example id="layouts-bind-constraint-example-1">
|
||||
<title>Constraining the size of a texture to
|
||||
the size of the stage using <type>ClutterBindConstraint</type></title>
|
||||
<programlisting>
|
||||
<xi:include href="examples/layouts-bind-constraint-stage.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The texture in this example is 100px smaller than the stage,
|
||||
leaving a border of visible stage around the texture; and the texture
|
||||
has a tiled image on it. The tiling changes as the texture changes
|
||||
size. Also note that two <type>ClutterAlignConstraints</type> are
|
||||
added to center the actor on the stage.</para>
|
||||
|
||||
<para>The result looks like this:</para>
|
||||
|
||||
<screenshot>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata format="PNG"
|
||||
fileref="images/layouts-bind-constraint-stage.png" />
|
||||
</imageobject>
|
||||
<alt>
|
||||
<para>A texture bound to the height and width of the
|
||||
stage using <type>ClutterBindConstraint</type></para>
|
||||
</alt>
|
||||
</mediaobject>
|
||||
</screenshot>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>Sizing constraints are a good solution in these cases:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Where you can't use a layout manager. For
|
||||
example, you can't apply a layout manager to the stage
|
||||
directly; so if you want to control the size of an actor
|
||||
based on the size of the stage (as in
|
||||
<link linkend="layouts-bind-constraint-example-1">the example
|
||||
above</link>), constraints are a good substitute for a layout
|
||||
manager .</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Where the layout of a UI is fairly simple (perhaps
|
||||
up to half a dozen actors) and fairly static. An example
|
||||
might be something like a text editor, where the arrangement
|
||||
of the UI (menu bar, toolbar, editing panel, footer) changes
|
||||
infrequently. Of course, it is possible to arrange top-level
|
||||
components using constraints, but still use layout
|
||||
managers inside individual components (e.g. a flow layout
|
||||
manager to manage buttons in the toolbar).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Where you have an actor whose size can change erratically,
|
||||
but you still want to be able to track its size to control
|
||||
another actor's size. An example might be an application like
|
||||
a drawing program, where a user can create their own actors:
|
||||
you might want the user to be able to describe loose, custom
|
||||
constraints between actors like "keep these actors at the
|
||||
same width", then allow those actors to be moved around and
|
||||
resized in a free-form way as a group. In this situation, a
|
||||
layout manager is too rigid and not appropriate;
|
||||
but adding <type>ClutterConstraints</type> to actors
|
||||
in response to user actions could work well.</para>
|
||||
|
||||
<para>The <link linkend="layouts-bind-constraint-example-2">sample
|
||||
code in the appendix</link> is the kind of thing you might include
|
||||
in a drawing program: you can resize a texture with a key press
|
||||
(<code>+</code> to increase size, <code>-</code> to decrease), and
|
||||
click on the actor to select/deselect it (a semi-transparent overlay is
|
||||
toggled on the texture). The size of the overlay is bound and
|
||||
aligned to the texture, so that it covers and slightly overlaps the
|
||||
texture regardless of its size.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<note>
|
||||
<para>You can bind an actor to a single dimension (just height or
|
||||
depth) of another actor: you don't have to bind both height
|
||||
and width. Also, you don't have to bind both dimensions of the
|
||||
target to the same source: for example, you could bind the target's
|
||||
height to one source (actor A) and its width to another source
|
||||
(actor B).</para>
|
||||
|
||||
<para>A <type>ClutterBindConstraint</type> can also be used to
|
||||
constrain a target actor's position on the <code>x</code> and
|
||||
<code>y</code> axes to the position of a source actor. This is
|
||||
covered in another recipe.</para>
|
||||
</note>
|
||||
|
||||
<section>
|
||||
<title>Another way to bind actors' sizes together</title>
|
||||
|
||||
<para>There is another way to control the size of a target
|
||||
actor, based on the size of a source: you can create a handler
|
||||
for the <code>allocation-changed</code> signal
|
||||
of the source, emitted when its size and/or position
|
||||
changes. This signal includes all the data
|
||||
about the source's new allocation (height, width, x and y
|
||||
coordindates), which the handler function can then use to
|
||||
resize the target.</para>
|
||||
|
||||
<para>Alternatively, if you're only interested in
|
||||
a change to width or height, you can create a handler
|
||||
for the <code>notify::width</code> or
|
||||
<code>notify::height</code> signal (respectively), and modify
|
||||
the target's width/height in the handler.</para>
|
||||
|
||||
<para>This approach may be useful if you need a type of
|
||||
control over alignment and size which is not possible using
|
||||
constraints alone (e.g. one actor's size should be
|
||||
a proportion of another's). See
|
||||
<link linkend="layouts-bind-constraint-example-3">the code in
|
||||
this section</link> for an example where the size
|
||||
of one actor is dynamically set to 10% more than the
|
||||
size of another.</para>
|
||||
|
||||
<note>
|
||||
<para><link linkend="actors-allocation-notify">This recipe</link>
|
||||
explains more about monitoring changes to an actor's size.</para>
|
||||
</note>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Full examples</title>
|
||||
|
||||
<example id="layouts-bind-constraint-example-2">
|
||||
<title>Creating an automatically-resizing overlay for a
|
||||
texture using <type>ClutterBindConstraint</type></title>
|
||||
<programlisting>
|
||||
<xi:include href="examples/layouts-bind-constraint-overlay.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example id="layouts-bind-constraint-example-3">
|
||||
<title>Using the <code>allocation-changed</code>
|
||||
signal of one actor to trigger proportional size changes in
|
||||
another</title>
|
||||
<programlisting>
|
||||
<xi:include href="examples/layouts-bind-constraint-allocation.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
Loading…
Reference in New Issue
Block a user