[IMP] web_editor: allow to flip/rotate image shapes

This commit adds the possibility to horizontally/vertically mirror and
rotate the shape of an image. We've introduced a new sub-option for
image shapes called "Transform". This new option is not available for
certain shapes:
- If the transform will have no effect (a vertically and horizontally
symmetrical shape).
- With some animations, if the animation is too complex for the system
to work.
- For "devices" shapes because they are too specific for the option to
work. And anyway "devices" shapes are already available horizontally
flipped in the list.

task-3094258

Part-of: odoo/odoo#119197
This commit is contained in:
Benjamin Vray
2023-09-05 18:44:17 +00:00
parent 76fcebd2ca
commit fbb395021d
3 changed files with 210 additions and 57 deletions
@@ -174,6 +174,12 @@ export class MediaDialog extends Component {
if (this.props.media.dataset.shapeColors) {
element.dataset.shapeColors = this.props.media.dataset.shapeColors;
}
if (this.props.media.dataset.shapeFlip) {
element.dataset.shapeFlip = this.props.media.dataset.shapeFlip;
}
if (this.props.media.dataset.shapeRotate) {
element.dataset.shapeRotate = this.props.media.dataset.shapeRotate;
}
}
}
for (const otherTab of Object.keys(TABS).filter(key => key !== this.state.activeTab)) {
@@ -6129,6 +6129,12 @@ registry.ImageTools = ImageHandlerOption.extend({
img.dataset.originalMimetype = img.dataset.mimetype;
img.dataset.mimetype = 'image/svg+xml';
}
// When the user selects a shape, we remove the data attributes
// that are not compatible with this shape.
if (saveData && !this._isTransformableShape()) {
delete img.dataset.shapeFlip;
delete img.dataset.shapeRotate;
}
}
} else {
// Re-applying the modifications and deleting the shapes
@@ -6136,6 +6142,8 @@ registry.ImageTools = ImageHandlerOption.extend({
delete img.dataset.shape;
delete img.dataset.shapeColors;
delete img.dataset.fileName;
delete img.dataset.shapeFlip;
delete img.dataset.shapeRotate;
if (saveData) {
img.dataset.mimetype = img.dataset.originalMimetype;
delete img.dataset.originalMimetype;
@@ -6158,6 +6166,38 @@ registry.ImageTools = ImageHandlerOption.extend({
await this._applyShapeAndColors(true, newColors);
img.classList.add('o_modified_image_to_save');
},
/**
* Flips the image shape horizontally.
*
* @see this.selectClass for parameters
*/
async setImgShapeFlipX(previewMode, widgetValue, params) {
await this._setImgShapeFlip("x");
},
/**
* Flips the image shape vertically.
*
* @see this.selectClass for parameters
*/
async setImgShapeFlipY(previewMode, widgetValue, params) {
await this._setImgShapeFlip("y");
},
/**
* Rotates the image shape 90 degrees to the left.
*
* @see this.selectClass for parameters
*/
async setImgShapeRotateLeft(previewMode, widgetValue, params) {
await this._setImgShapeRotate(-90);
},
/**
* Rotates the image shape 90 degrees to the right.
*
* @see this.selectClass for parameters
*/
async setImgShapeRotateRight(previewMode, widgetValue, params) {
await this._setImgShapeRotate(90);
},
//--------------------------------------------------------------------------
// Private
@@ -6247,6 +6287,26 @@ registry.ImageTools = ImageHandlerOption.extend({
const initialImageWidth = img.naturalWidth;
const svg = new DOMParser().parseFromString(svgText, 'image/svg+xml').documentElement;
// Modifies the SVG according to the "flip" or/and "rotate" options.
const shapeFlip = img.dataset.shapeFlip || "";
const shapeRotate = img.dataset.shapeRotate || 0;
if ((shapeFlip || shapeRotate) && this._isTransformableShape()) {
let shapeTransformValues = [];
if (shapeFlip) { // Possible values => "x", "y", "xy"
shapeTransformValues.push(`scale${shapeFlip === "x" ? "X" : shapeFlip === "y" ? "Y" : ""}(-1)`);
}
if (shapeRotate) { // Possible values => "90", "180", "270"
shapeTransformValues.push(`rotate(${shapeRotate}deg)`);
}
// "transform-origin: center;" does not work on "#filterPath". But
// since its dimension is 1px * 1px the following solution works.
const transformOrigin = "transform-origin: 0.5px 0.5px;";
// Applies the transformation values to the path used to create a
// mask over the SVG image.
svg.querySelector("#filterPath").setAttribute("style", `transform: ${shapeTransformValues.join(" ")}; ${transformOrigin}`);
}
const svgAspectRatio = parseInt(svg.getAttribute('width')) / parseInt(svg.getAttribute('height'));
// We will store the image in base64 inside the SVG.
// applyModifications will return a dataURL with the current filters
@@ -6356,6 +6416,10 @@ registry.ImageTools = ImageHandlerOption.extend({
const img = this._getImg();
return isImageSupportedForStyle(img) || this._isImageSupportedForProcessing(img);
}
if (["img_shape_transform_flip_x_opt", "img_shape_transform_flip_y_opt",
"img_shape_transform_rotate_x_opt", "img_shape_transform_rotate_y_opt"].includes(params.name)) {
return this._isTransformableShape();
}
return this._super(...arguments);
},
/**
@@ -6389,6 +6453,14 @@ registry.ImageTools = ImageHandlerOption.extend({
const img = this._getImg();
return (img.dataset.shapeColors && img.dataset.shapeColors.split(';')[parseInt(params.colorId)]) || '';
}
case 'setImgShapeFlipX': {
const imgEl = this._getImg();
return imgEl.dataset.shapeFlip?.includes("x") || "";
}
case 'setImgShapeFlipY': {
const imgEl = this._getImg();
return imgEl.dataset.shapeFlip?.includes("y") || "";
}
}
return this._super(...arguments);
},
@@ -6504,6 +6576,8 @@ registry.ImageTools = ImageHandlerOption.extend({
delete img.dataset.shapeColors;
delete img.dataset.fileName;
delete img.dataset.originalMimetype;
delete img.dataset.shapeFlip;
delete img.dataset.shapeRotate;
return;
}
if (img.dataset.mimetype !== "image/svg+xml") {
@@ -6533,6 +6607,69 @@ registry.ImageTools = ImageHandlerOption.extend({
}
return this._super(...arguments);
},
/**
* Flips the image shape (vertically or/and horizontally).
*
* @private
* @param {string} flipValue image shape flip value
*/
async _setImgShapeFlip(flipValue) {
const imgEl = this._getImg();
const currentFlipValue = imgEl.dataset.shapeFlip || "";
const newFlipValue = currentFlipValue.includes(flipValue)
? currentFlipValue.replace(flipValue, "")
: currentFlipValue + flipValue;
if (newFlipValue) {
imgEl.dataset.shapeFlip = newFlipValue === "yx" ? "xy" : newFlipValue;
} else {
delete imgEl.dataset.shapeFlip;
}
await this._applyShapeAndColors(true, imgEl.dataset.shapeColors?.split(";"));
imgEl.classList.add("o_modified_image_to_save");
},
/**
* Rotates the image shape 90 degrees.
*
* @private
* @param {integer} rotation rotation value
*/
async _setImgShapeRotate(rotation) {
const imgEl = this._getImg();
const currentRotateValue = parseInt(imgEl.dataset.shapeRotate) || 0;
const newRotateValue = (currentRotateValue + rotation + 360) % 360;
if (newRotateValue) {
imgEl.dataset.shapeRotate = newRotateValue;
} else {
delete imgEl.dataset.shapeRotate;
}
await this._applyShapeAndColors(true, imgEl.dataset.shapeColors?.split(";"));
imgEl.classList.add("o_modified_image_to_save");
},
/**
* Checks if the shape is in the "devices" category.
*
* @private
* @returns {boolean}
*/
_isDeviceShape() {
const imgEl = this._getImg();
const shapeName = imgEl.dataset.shape;
if (!shapeName) {
return false;
}
const shapeCategory = imgEl.dataset.shape.split("/")[1];
return shapeCategory === "devices";
},
/**
* Checks if the shape is transformable.
*
* @private
* @returns {boolean}
*/
_isTransformableShape() {
const shapeImgWidget = this._requestUserValueWidgets("shape_img_opt")[0];
return (shapeImgWidget && !shapeImgWidget.getMethodsParams().noTransform) && !this._isDeviceShape();
},
//--------------------------------------------------------------------------
// Handlers
+67 -57
View File
@@ -518,17 +518,17 @@
<div class="o_scroll_shapes_basic">
<we-title class="">Geometrics</we-title>
<we-select-page string="Geometrics">
<we-button data-set-img-shape="web_editor/geometric/geo_square" data-select-label="Square"/>
<we-button data-set-img-shape="web_editor/geometric/geo_shuriken" data-select-label="Shuriken"/>
<we-button data-set-img-shape="web_editor/geometric/geo_diamond" data-select-label="Diamond"/>
<we-button data-set-img-shape="web_editor/geometric/geo_square" data-select-label="Square" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric/geo_shuriken" data-select-label="Shuriken" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric/geo_diamond" data-select-label="Diamond" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric/geo_triangle" data-select-label="Triangle"/>
<we-button data-set-img-shape="web_editor/geometric/geo_cornered_triangle" data-select-label="Corner Triangle"/>
<we-button data-set-img-shape="web_editor/geometric/geo_pentagon" data-select-label="Pentagon"/>
<we-button data-set-img-shape="web_editor/geometric/geo_hexagon" data-select-label="Hexagon"/>
<we-button data-set-img-shape="web_editor/geometric/geo_heptagon" data-select-label="Heptagon"/>
<we-button data-set-img-shape="web_editor/geometric/geo_star" data-select-label="Star 1"/>
<we-button data-set-img-shape="web_editor/geometric/geo_star_8pin" data-select-label="Star 2"/>
<we-button data-set-img-shape="web_editor/geometric/geo_star_16pin" data-select-label="Star 3"/>
<we-button data-set-img-shape="web_editor/geometric/geo_star_8pin" data-select-label="Star 2" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric/geo_star_16pin" data-select-label="Star 3" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric/geo_slanted" data-select-label="Slanted"/>
<we-button data-set-img-shape="web_editor/geometric/geo_emerald" data-select-label="Emerald"/>
<we-button data-set-img-shape="web_editor/geometric/geo_tetris" data-select-label="Tetris"/>
@@ -549,18 +549,18 @@
<we-title>Geometrics Rounded</we-title>
<we-select-page string="Geometrics Rounded">
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_circle" data-select-label="Circle"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_square" data-select-label="Square (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_diamond" data-select-label="Diamond (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_shuriken" data-select-label="Shuriken (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_circle" data-select-label="Circle" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_square" data-select-label="Square (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_diamond" data-select-label="Diamond (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_shuriken" data-select-label="Shuriken (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_triangle" data-select-label="Triangle (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_pentagon" data-select-label="Pentagon (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_hexagon" data-select-label="Hexagon (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_heptagon" data-select-label="Heptagon (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star" data-select-label="Star 1 (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star_7pin" data-select-label="Star 2 (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star_8pin" data-select-label="Star 3 (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star_16pin" data-select-label="Star 4 (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star_8pin" data-select-label="Star 3 (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_star_16pin" data-select-label="Star 4 (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_emerald" data-select-label="Emerald (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_lemon" data-select-label="Lemon (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_tear" data-select-label="Tear (R)"/>
@@ -569,8 +569,8 @@
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_cornered" data-select-label="Cornered"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_door" data-select-label="Door (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_sonar" data-select-label="Sonar (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_clover" data-select-label="Clover (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_bread" data-select-label="Bread (R)"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_clover" data-select-label="Clover (R)" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_bread" data-select-label="Bread (R)" data-no-transform="true"/>
<t t-if="not no_animations">
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_square_1" data-select-label="Square 1 (R)" data-animated="true"/>
<we-button data-set-img-shape="web_editor/geometric_round/geo_round_square_2" data-select-label="Square 2 (R)" data-animated="true"/>
@@ -588,7 +588,7 @@
<we-button data-set-img-shape="web_editor/panel/panel_duo_step_pill" data-select-label="Duo Step Pill"/>
<we-button data-set-img-shape="web_editor/panel/panel_trio_in_r" data-select-label="Trio In (R)"/>
<we-button data-set-img-shape="web_editor/panel/panel_trio_out_r" data-select-label="Trio Out (R)"/>
<we-button data-set-img-shape="web_editor/panel/panel_window" data-select-label="Window"/>
<we-button data-set-img-shape="web_editor/panel/panel_window" data-select-label="Window" data-no-transform="true"/>
</we-select-page>
<we-title>Composites</we-title>
@@ -611,67 +611,67 @@
<we-title>Composition</we-title>
<we-select-page string="Composition">
<we-button data-set-img-shape="web_editor/composition/composition_organic_line" data-select-label="Organic Line"/>
<we-button data-set-img-shape="web_editor/composition/composition_oval_line" data-select-label="Oval Line"/>
<we-button data-set-img-shape="web_editor/composition/composition_organic_line" data-select-label="Organic Line" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_oval_line" data-select-label="Oval Line" data-no-transform="true"/>
<t t-if="not no_animations">
<we-button data-set-img-shape="web_editor/composition/composition_triangle_line" data-select-label="Triangle Line" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_1" data-select-label="Line 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_3" data-select-label="Line 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_2" data-select-label="Line 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_mixed_1" data-select-label="Mixed 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_mixed_2" data-select-label="Mixed 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_planet_1" data-select-label="Planet 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_planet_2" data-select-label="Planet 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_1" data-select-label="Square Dot 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_2" data-select-label="Square Dot 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_3" data-select-label="Square Dot 3" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_4" data-select-label="Square Dot 4" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_line" data-select-label="Square Line" data-animated="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_triangle_line" data-select-label="Triangle Line" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_1" data-select-label="Line 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_3" data-select-label="Line 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_line_2" data-select-label="Line 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_mixed_1" data-select-label="Mixed 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_mixed_2" data-select-label="Mixed 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_planet_1" data-select-label="Planet 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_planet_2" data-select-label="Planet 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_1" data-select-label="Square Dot 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_2" data-select-label="Square Dot 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_3" data-select-label="Square Dot 3" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_4" data-select-label="Square Dot 4" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/composition/composition_square_line" data-select-label="Square Line" data-animated="true" data-no-transform="true"/>
</t>
</we-select-page>
<we-title>Patterns</we-title>
<we-select-page string="Patterns">
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_cross" data-select-label="Organic Cross"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_caps" data-select-label="Organic Caps"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_oval_zebra" data-select-label="Oval Zebra"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_cross" data-select-label="Organic Cross" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_caps" data-select-label="Organic Caps" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_oval_zebra" data-select-label="Oval Zebra" data-no-transform="true"/>
<t t-if="not no_animations">
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_1" data-select-label="Wave 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_line_star" data-select-label="Star" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_line_sun" data-select-label="Sun" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_2" data-select-label="Wave 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_3" data-select-label="Wave 3" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_point" data-select-label="Point" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_dot" data-select-label="Organic Dot" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_labyrinth" data-select-label="Labyrinth" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_circuit" data-select-label="Circuit" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_4" data-select-label="Wave 4" data-animated="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_1" data-select-label="Wave 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_line_star" data-select-label="Star" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_line_sun" data-select-label="Sun" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_2" data-select-label="Wave 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_3" data-select-label="Wave 3" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_point" data-select-label="Point" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_organic_dot" data-select-label="Organic Dot" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_labyrinth" data-select-label="Labyrinth" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_circuit" data-select-label="Circuit" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/pattern/pattern_wave_4" data-select-label="Wave 4" data-animated="true" data-no-transform="true"/>
</t>
</we-select-page>
<we-title>Solids</we-title>
<we-select-page string="Solids">
<we-button data-set-img-shape="web_editor/solid/solid_blob_1" data-select-label="Blob 1"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_2" data-select-label="Blob 2"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_3" data-select-label="Blob 3"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_4" data-select-label="Blob 4"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_1" data-select-label="Blob 1" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_2" data-select-label="Blob 2" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_3" data-select-label="Blob 3" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_4" data-select-label="Blob 4" data-no-transform="true"/>
<t t-if="not no_animations">
<we-button data-set-img-shape="web_editor/solid/solid_blob_5" data-select-label="Blob 5" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_shadow_1" data-select-label="Blob Shadow 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_shadow_2" data-select-label="Blob Shadow 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_1" data-select-label="Square Shadow 1" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_2" data-select-label="Square Shadow 2" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_3" data-select-label="Square Shadow 3" data-animated="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_5" data-select-label="Blob 5" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_shadow_1" data-select-label="Blob Shadow 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_blob_shadow_2" data-select-label="Blob Shadow 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_1" data-select-label="Square Shadow 1" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_2" data-select-label="Square Shadow 2" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/solid/solid_square_3" data-select-label="Square Shadow 3" data-animated="true" data-no-transform="true"/>
</t>
</we-select-page>
<we-title>Specials</we-title>
<we-select-page string="Specials" t-if="not no_animations">
<we-button data-set-img-shape="web_editor/special/special_speed" data-select-label="Speed" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_rain" data-select-label="Rain" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_snow" data-select-label="Snow" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_layered" data-select-label="Layered" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_filter" data-select-label="Filter" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_speed" data-select-label="Speed" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/special/special_rain" data-select-label="Rain" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/special/special_snow" data-select-label="Snow" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/special/special_layered" data-select-label="Layered" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/special/special_filter" data-select-label="Filter" data-animated="true" data-no-transform="true"/>
<we-button data-set-img-shape="web_editor/special/special_flag" data-select-label="Flag" data-animated="true"/>
<we-button data-set-img-shape="web_editor/special/special_organic" data-select-label="Organic" data-animated="true"/>
</we-select-page>
@@ -719,6 +719,16 @@
<we-colorpicker data-set-img-shape-color="true" data-dependencies="shape_img_opt" data-name="img-shape-color-3" data-color-id="3"/>
<we-colorpicker data-set-img-shape-color="true" data-dependencies="shape_img_opt" data-name="img-shape-color-4" data-color-id="4"/>
</we-row>
<we-row string="Transform" class="o_we_sublevel_1">
<we-button class="oi oi-fw oi-arrows-h" data-name="img_shape_transform_flip_x_opt" data-set-img-shape-flip-x="true"
data-no-preview="true" data-dependencies="shape_img_opt" title="Horizontal mirror"/>
<we-button class="oi oi-fw oi-arrows-v" data-name="img_shape_transform_flip_y_opt" data-set-img-shape-flip-y="true"
data-no-preview="true" data-dependencies="shape_img_opt" title="Vertical mirror"/>
<we-button class="fa fa-fw fa-rotate-left" data-name="img_shape_transform_rotate_x_opt" data-set-img-shape-rotate-left="true"
data-no-preview="true" data-dependencies="shape_img_opt" title="Rotate left"/>
<we-button class="fa fa-fw fa-rotate-right" data-name="img_shape_transform_rotate_y_opt" data-set-img-shape-rotate-right="true"
data-no-preview="true" data-dependencies="shape_img_opt" title="Rotate right"/>
</we-row>
</div>
<we-row string="Transform">