🔧 Add vertical alignment for text shapes

This commit is contained in:
Elena Torro 2025-06-23 16:01:03 +02:00
parent 0010d61ae2
commit 134fb1ab4c
8 changed files with 2436 additions and 1 deletions

File diff suppressed because it is too large Load diff

View file

@ -162,3 +162,16 @@ test("Renders a file with multiple emoji", async ({ page }) => {
await workspace.waitForFirstRender(); await workspace.waitForFirstRender();
await expect(workspace.canvas).toHaveScreenshot(); await expect(workspace.canvas).toHaveScreenshot();
}); });
test("Renders a file with texts with different alignments", async ({ page }) => {
const workspace = new WasmWorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.mockGetFile("render-wasm/get-file-text-align.json");
await workspace.goToWorkspace({
id: "692f368b-63ca-8141-8006-62925640b827",
pageId: "692f368b-63ca-8141-8006-62925640b828",
});
await workspace.waitForFirstRender();
await expect(workspace.canvas).toHaveScreenshot();
});

View file

@ -236,6 +236,7 @@
(defn set-shape-text-images (defn set-shape-text-images
[shape-id content] [shape-id content]
(let [paragraph-set (first (get content :children)) (let [paragraph-set (first (get content :children))
paragraphs (get paragraph-set :children)] paragraphs (get paragraph-set :children)]
(->> paragraphs (->> paragraphs
@ -357,6 +358,10 @@
;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html ;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html
(h/call wasm/internal-module "_set_shape_blend_mode" (sr/translate-blend-mode blend-mode))) (h/call wasm/internal-module "_set_shape_blend_mode" (sr/translate-blend-mode blend-mode)))
(defn set-shape-vertical-align
[vertical-align]
(h/call wasm/internal-module "_set_shape_vertical_align" (sr/serialize-vertical-align vertical-align)))
(defn set-shape-opacity (defn set-shape-opacity
[opacity] [opacity]
(h/call wasm/internal-module "_set_shape_opacity" (or opacity 1))) (h/call wasm/internal-module "_set_shape_opacity" (or opacity 1)))
@ -630,6 +635,8 @@
(defn set-shape-text-content (defn set-shape-text-content
[shape-id content] [shape-id content]
(h/call wasm/internal-module "_clear_shape_text") (h/call wasm/internal-module "_clear_shape_text")
(set-shape-vertical-align (dm/get-prop content :vertical-align))
(let [paragraph-set (first (dm/get-prop content :children)) (let [paragraph-set (first (dm/get-prop content :children))
paragraphs (dm/get-prop paragraph-set :children) paragraphs (dm/get-prop paragraph-set :children)
fonts (fonts/get-content-fonts content) fonts (fonts/get-content-fonts content)
@ -752,6 +759,7 @@
(when (some? shadows) (set-shape-shadows shadows)) (when (some? shadows) (set-shape-shadows shadows))
(when (= type :text) (when (= type :text)
(set-shape-grow-type grow-type)) (set-shape-grow-type grow-type))
(when (or (ctl/any-layout? shape) (when (or (ctl/any-layout? shape)
(ctl/any-layout-immediate-child? objects shape)) (ctl/any-layout-immediate-child? objects shape))
(set-layout-child shape)) (set-layout-child shape))

View file

@ -295,6 +295,10 @@
[value enum-map] [value enum-map]
(get enum-map value 0)) (get enum-map value 0))
(defn serialize-vertical-align
[vertical-align]
(serialize-enum vertical-align {"top" 0 "center" 1 "bottom" 2}))
(defn serialize-text-align (defn serialize-text-align
[text-align] [text-align]
(serialize-enum text-align {"left" 0 "center" 1 "right" 2 "justify" 3})) (serialize-enum text-align {"left" 0 "center" 1 "right" 2 "justify" 3}))

View file

@ -19,6 +19,7 @@ use math::{Bounds, Matrix};
use mem::SerializableResult; use mem::SerializableResult;
use shapes::{ use shapes::{
BoolType, ConstraintH, ConstraintV, StructureEntry, StructureEntryType, TransformEntry, Type, BoolType, ConstraintH, ConstraintV, StructureEntry, StructureEntryType, TransformEntry, Type,
VerticalAlign,
}; };
use skia_safe as skia; use skia_safe as skia;
use state::State; use state::State;
@ -346,6 +347,13 @@ pub extern "C" fn set_shape_blend_mode(mode: i32) {
}); });
} }
#[no_mangle]
pub extern "C" fn set_shape_vertical_align(align: u8) {
with_current_shape!(state, |shape: &mut Shape| {
shape.set_vertical_align(VerticalAlign::from(align));
});
}
#[no_mangle] #[no_mangle]
pub extern "C" fn set_shape_opacity(opacity: f32) { pub extern "C" fn set_shape_opacity(opacity: f32) {
with_current_shape!(state, |shape: &mut Shape| { with_current_shape!(state, |shape: &mut Shape| {

View file

@ -1,4 +1,5 @@
use super::{RenderState, Shape, SurfaceId}; use super::{RenderState, Shape, SurfaceId};
use crate::shapes::VerticalAlign;
use skia_safe::{textlayout::Paragraph, Paint, Path}; use skia_safe::{textlayout::Paragraph, Paint, Path};
pub fn render( pub fn render(
@ -11,8 +12,16 @@ pub fn render(
.surfaces .surfaces
.canvas(surface_id.unwrap_or(SurfaceId::Fills)); .canvas(surface_id.unwrap_or(SurfaceId::Fills));
let container_height = shape.selrect().height();
for group in paragraphs { for group in paragraphs {
let mut offset_y = 0.0; let total_paragraphs_height: f32 = group.iter().map(|p| p.height()).sum();
let mut offset_y = match shape.vertical_align() {
VerticalAlign::Center => (container_height - total_paragraphs_height) / 2.0,
VerticalAlign::Bottom => container_height - total_paragraphs_height,
_ => 0.0,
};
for skia_paragraph in group { for skia_paragraph in group {
let xy = (shape.selrect().x(), shape.selrect.y() + offset_y); let xy = (shape.selrect().x(), shape.selrect.y() + offset_y);
skia_paragraph.paint(canvas, xy); skia_paragraph.paint(canvas, xy);

View file

@ -156,6 +156,24 @@ impl ConstraintH {
} }
} }
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum VerticalAlign {
Top,
Center,
Bottom,
}
impl VerticalAlign {
pub fn from(value: u8) -> Self {
match value {
0 => Self::Top,
1 => Self::Center,
2 => Self::Bottom,
_ => Self::Top,
}
}
}
#[derive(Debug, Clone, PartialEq, Copy)] #[derive(Debug, Clone, PartialEq, Copy)]
pub enum ConstraintV { pub enum ConstraintV {
Top, Top,
@ -195,6 +213,7 @@ pub struct Shape {
pub fills: Vec<Fill>, pub fills: Vec<Fill>,
pub strokes: Vec<Stroke>, pub strokes: Vec<Stroke>,
pub blend_mode: BlendMode, pub blend_mode: BlendMode,
pub vertical_align: VerticalAlign,
pub blur: Blur, pub blur: Blur,
pub opacity: f32, pub opacity: f32,
pub hidden: bool, pub hidden: bool,
@ -221,6 +240,7 @@ impl Shape {
fills: Vec::with_capacity(1), fills: Vec::with_capacity(1),
strokes: Vec::with_capacity(1), strokes: Vec::with_capacity(1),
blend_mode: BlendMode::default(), blend_mode: BlendMode::default(),
vertical_align: VerticalAlign::Top,
opacity: 1., opacity: 1.,
hidden: false, hidden: false,
blur: Blur::default(), blur: Blur::default(),
@ -312,6 +332,14 @@ impl Shape {
self.opacity = opacity; self.opacity = opacity;
} }
pub fn set_vertical_align(&mut self, align: VerticalAlign) {
self.vertical_align = align;
}
pub fn vertical_align(&self) -> VerticalAlign {
self.vertical_align
}
pub fn set_constraint_h(&mut self, constraint: Option<ConstraintH>) { pub fn set_constraint_h(&mut self, constraint: Option<ConstraintH>) {
self.constraint_h = constraint; self.constraint_h = constraint;
} }