Compare commits
2 Commits
wip/matthi
...
wip/otte/n
Author | SHA1 | Date | |
---|---|---|---|
|
a59e1254ef | ||
|
43802f8b07 |
@@ -61,8 +61,10 @@ struct _NodeEditorWindow
|
||||
GtkWidget *scale_scale;
|
||||
|
||||
GtkWidget *renderer_listbox;
|
||||
GListStore *saved_nodes;
|
||||
GListStore *renderers;
|
||||
GskRenderNode *node;
|
||||
GtkAdjustment *compare_progress;
|
||||
|
||||
GFileMonitor *file_monitor;
|
||||
|
||||
@@ -208,7 +210,7 @@ text_changed (GtkTextBuffer *buffer,
|
||||
GtkSnapshot *snapshot;
|
||||
GdkPaintable *paintable;
|
||||
graphene_rect_t bounds;
|
||||
guint i;
|
||||
guint i, n;
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gsk_render_node_get_bounds (big_node, &bounds);
|
||||
@@ -224,6 +226,8 @@ text_changed (GtkTextBuffer *buffer,
|
||||
gtk_snapshot_append_node (snapshot, self->node);
|
||||
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes));
|
||||
g_list_store_splice (self->saved_nodes, MAX (n, 1) - 1, MIN (n, 1), (gpointer[1]) { paintable }, 1);
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->renderers)); i++)
|
||||
{
|
||||
gpointer item = g_list_model_get_item (G_LIST_MODEL (self->renderers), i);
|
||||
@@ -308,6 +312,17 @@ text_changed (GtkTextBuffer *buffer,
|
||||
&start, &end);
|
||||
}
|
||||
|
||||
static void
|
||||
stash_current_node (NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *last;
|
||||
|
||||
last = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes),
|
||||
g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes)) - 1);
|
||||
g_list_store_append (self->saved_nodes, last);
|
||||
g_object_unref (last);
|
||||
}
|
||||
|
||||
static void
|
||||
scale_changed (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
@@ -377,7 +392,8 @@ text_view_query_tooltip_cb (GtkWidget *widget,
|
||||
|
||||
static gboolean
|
||||
load_bytes (NodeEditorWindow *self,
|
||||
GBytes *bytes);
|
||||
GBytes *bytes,
|
||||
gboolean stash);
|
||||
|
||||
static void
|
||||
load_error (NodeEditorWindow *self,
|
||||
@@ -395,7 +411,7 @@ load_error (NodeEditorWindow *self,
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
|
||||
load_bytes (self, bytes);
|
||||
load_bytes (self, bytes, TRUE);
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
g_object_unref (layout);
|
||||
@@ -403,7 +419,8 @@ load_error (NodeEditorWindow *self,
|
||||
|
||||
static gboolean
|
||||
load_bytes (NodeEditorWindow *self,
|
||||
GBytes *bytes)
|
||||
GBytes *bytes,
|
||||
gboolean stash)
|
||||
{
|
||||
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
|
||||
{
|
||||
@@ -411,6 +428,9 @@ load_bytes (NodeEditorWindow *self,
|
||||
g_bytes_unref (bytes);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (stash)
|
||||
stash_current_node (self);
|
||||
|
||||
gtk_text_buffer_set_text (self->text_buffer,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
@@ -436,7 +456,29 @@ load_file_contents (NodeEditorWindow *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return load_bytes (self, bytes);
|
||||
return load_bytes (self, bytes, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
saved_node_activate_cb (GtkListView *listview,
|
||||
guint pos,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
GtkSnapshot *snapshot;
|
||||
GskRenderNode *node;
|
||||
GBytes *bytes;
|
||||
|
||||
paintable = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), pos);
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (paintable, snapshot, gdk_paintable_get_intrinsic_width (paintable), gdk_paintable_get_intrinsic_height (paintable));
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
load_bytes (self, bytes, TRUE);
|
||||
|
||||
g_object_unref (paintable);
|
||||
}
|
||||
|
||||
static GdkContentProvider *
|
||||
@@ -465,7 +507,7 @@ on_picture_drop_read_done_cb (GObject *source,
|
||||
if (g_output_stream_splice_finish (stream, res, NULL) >= 0)
|
||||
{
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
|
||||
if (load_bytes (self, bytes))
|
||||
if (load_bytes (self, bytes, TRUE))
|
||||
action = GDK_ACTION_COPY;
|
||||
}
|
||||
|
||||
@@ -639,6 +681,10 @@ save_response_cb (GObject *source,
|
||||
g_object_unref (alert);
|
||||
g_error_free (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
stash_current_node (self);
|
||||
}
|
||||
|
||||
g_free (text);
|
||||
g_object_unref (file);
|
||||
@@ -1057,6 +1103,8 @@ testcase_save_clicked_cb (GtkWidget *button,
|
||||
gtk_editable_set_text (GTK_EDITABLE (self->testcase_name_entry), "");
|
||||
gtk_popover_popdown (GTK_POPOVER (self->testcase_popover));
|
||||
|
||||
stash_current_node (self);
|
||||
|
||||
out:
|
||||
g_free (text);
|
||||
g_free (png_file);
|
||||
@@ -1064,9 +1112,147 @@ out:
|
||||
g_free (source_dir);
|
||||
}
|
||||
|
||||
static inline double
|
||||
double_transform (double start,
|
||||
double end,
|
||||
double progress)
|
||||
{
|
||||
return start + (end - start) * progress;
|
||||
}
|
||||
|
||||
static void
|
||||
dark_mode_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
rgba_transform (GdkRGBA *result,
|
||||
const GdkRGBA *start,
|
||||
const GdkRGBA *end,
|
||||
double progress)
|
||||
{
|
||||
result->alpha = CLAMP (double_transform (start->alpha, end->alpha, progress), 0, 1);
|
||||
|
||||
if (result->alpha <= 0.0)
|
||||
{
|
||||
result->red = result->green = result->blue = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result->red = CLAMP (double_transform (start->red * start->alpha, end->red * end->alpha, progress), 0, 1) / result->alpha;
|
||||
result->green = CLAMP (double_transform (start->green * start->alpha, end->green * end->alpha, progress), 0, 1) / result->alpha;
|
||||
result->blue = CLAMP (double_transform (start->blue * start->alpha, end->blue * end->alpha, progress), 0, 1) / result->alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
render_node_transform (GskRenderNode *start,
|
||||
GskRenderNode *end,
|
||||
double progress)
|
||||
{
|
||||
GskRenderNodeType start_type, end_type;
|
||||
|
||||
start_type = gsk_render_node_get_node_type (start);
|
||||
end_type = gsk_render_node_get_node_type (end);
|
||||
|
||||
if (start_type == end_type)
|
||||
{
|
||||
switch (start_type)
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
graphene_rect_t start_bounds, end_bounds, bounds;
|
||||
|
||||
rgba_transform (&rgba, gsk_color_node_get_color (start), gsk_color_node_get_color (end), progress);
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
return gsk_color_node_new (&rgba, &bounds);
|
||||
}
|
||||
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
case GSK_LINEAR_GRADIENT_NODE:
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
case GSK_RADIAL_GRADIENT_NODE:
|
||||
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
||||
case GSK_CONIC_GRADIENT_NODE:
|
||||
case GSK_BORDER_NODE:
|
||||
case GSK_INSET_SHADOW_NODE:
|
||||
case GSK_OUTSET_SHADOW_NODE:
|
||||
case GSK_TRANSFORM_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
case GSK_COLOR_MATRIX_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_REPEAT_NODE:
|
||||
case GSK_CLIP_NODE:
|
||||
case GSK_ROUNDED_CLIP_NODE:
|
||||
case GSK_SHADOW_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_GL_SHADER_NODE:
|
||||
case GSK_CONTAINER_NODE:
|
||||
case GSK_DEBUG_NODE:
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gsk_cross_fade_node_new (start, end, progress);
|
||||
}
|
||||
|
||||
static void
|
||||
update_compare (NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *from, *to;
|
||||
GtkSnapshot *snapshot;
|
||||
GskRenderNode *node, *node_from, *node_to;
|
||||
GBytes *bytes;
|
||||
guint n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes));
|
||||
|
||||
from = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), n - 3);
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (from, snapshot, gdk_paintable_get_intrinsic_width (from), gdk_paintable_get_intrinsic_height (from));
|
||||
node_from = gtk_snapshot_free_to_node (snapshot);
|
||||
g_object_unref (from);
|
||||
|
||||
to = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), n - 2);
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (to, snapshot, gdk_paintable_get_intrinsic_width (to), gdk_paintable_get_intrinsic_height (to));
|
||||
node_to = gtk_snapshot_free_to_node (snapshot);
|
||||
g_object_unref (to);
|
||||
|
||||
node = render_node_transform (node_from, node_to, gtk_adjustment_get_value (self->compare_progress));
|
||||
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
load_bytes (self, bytes, FALSE);
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
gsk_render_node_unref (node_to);
|
||||
gsk_render_node_unref (node_from);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_toggled_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
if (!gtk_toggle_button_get_active (button))
|
||||
return;
|
||||
|
||||
stash_current_node (self);
|
||||
|
||||
update_compare (self);
|
||||
}
|
||||
|
||||
static void
|
||||
dark_mode_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
g_object_set (gtk_widget_get_settings (GTK_WIDGET (self)),
|
||||
@@ -1187,6 +1373,7 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, compare_progress);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, renderer_listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_popover);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_error_label);
|
||||
@@ -1194,17 +1381,21 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_name_entry);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_save_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, scale_scale);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, saved_nodes);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, text_view_query_tooltip_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, saved_node_activate_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, open_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, save_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, export_image_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, clip_image_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, testcase_save_clicked_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, testcase_name_entry_changed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, compare_toggled_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, dark_mode_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drag_prepare_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drop_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, update_compare);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
|
@@ -157,6 +157,16 @@
|
||||
<signal name="notify::active" handler="dark_mode_cb" swapped="0"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkToggleButton" id="compare_button">
|
||||
<property name="focus-on-click">0</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="has-frame">0</property>
|
||||
<property name="icon-name">emblem-synchronizing-symbolic</property>
|
||||
<property name="tooltip-text" translatable="yes">Compare the last 2 items</property>
|
||||
<signal name="notify::active" handler="compare_toggled_cb" swapped="0"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkScaleButton" id="scale_scale">
|
||||
<property name="focus-on-click">0</property>
|
||||
@@ -177,78 +187,150 @@
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="shrink-start-child">false</property>
|
||||
<property name="shrink-end-child">false</property>
|
||||
<property name="position">400</property>
|
||||
<property name="start-child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="wrap-mode">word</property>
|
||||
<property name="monospace">1</property>
|
||||
<property name="top-margin">6</property>
|
||||
<property name="left-margin">6</property>
|
||||
<property name="right-margin">6</property>
|
||||
<property name="bottom-margin">6</property>
|
||||
<property name="has-tooltip">1</property>
|
||||
<signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
|
||||
<style>
|
||||
<class name="editor" />
|
||||
</style>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkListView">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="single-click-activate">1</property>
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">
|
||||
<object class="GListStore" id="saved_nodes" />
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="paintable">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="activate" handler="saved_node_activate_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
<property name="end-child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="shrink-start-child">false</property>
|
||||
<property name="shrink-end-child">false</property>
|
||||
<property name="position">400</property>
|
||||
<property name="start-child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="min-content-width">100</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="wrap-mode">word</property>
|
||||
<property name="monospace">1</property>
|
||||
<property name="top-margin">6</property>
|
||||
<property name="left-margin">6</property>
|
||||
<property name="right-margin">6</property>
|
||||
<property name="bottom-margin">6</property>
|
||||
<property name="has-tooltip">1</property>
|
||||
<signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
|
||||
<style>
|
||||
<class name="editor" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="end-child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkOverlay">
|
||||
<child>
|
||||
<object class="GtkPicture" id="picture">
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="min-content-width">100</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="actions">copy</property>
|
||||
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
|
||||
<object class="GtkViewport">
|
||||
<child>
|
||||
<object class="GtkPicture" id="picture">
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="actions">copy</property>
|
||||
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDropTargetAsync">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">application/x-gtk-render-node</property>
|
||||
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkBox">
|
||||
<property name="margin-top">16</property>
|
||||
<property name="margin-start">16</property>
|
||||
<property name="margin-end">16</property>
|
||||
<property name="margin-bottom">16</property>
|
||||
<property name="halign">fill</property>
|
||||
<property name="valign">end</property>
|
||||
<property name="visible" bind-source="compare_button" bind-property="active">0</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="width-request">200</property>
|
||||
<property name="draw-value">0</property>
|
||||
<property name="adjustment">
|
||||
<object class="GtkAdjustment" id="compare_progress">
|
||||
<property name="upper">1</property>
|
||||
<property name="value">0.5</property>
|
||||
<property name="step-increment">0.01</property>
|
||||
<property name="page-increment">0.1</property>
|
||||
<signal name="notify::value" handler="update_compare" swapped="yes"/>
|
||||
</object>
|
||||
</property>
|
||||
<property name="hexpand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkDropTargetAsync">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">application/x-gtk-render-node</property>
|
||||
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
|
||||
<object class="GtkListBox" id="renderer_listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="renderer_listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user