mirror of
https://github.com/penpot/penpot.git
synced 2026-09-07 20:49:37 +00:00
🐛 Fix font and variant dropdowns on mixed text styles (#10520)
* 🐛 Fix dropdown shown Mixed Font Families for same family with different variant * 🐛 Fix variants dropdown appearing blank on mixed variants but same family * ✨ Add playwright test for mixed font families/variants
This commit is contained in:
parent
64ba70e6f3
commit
6d458c80a1
@ -0,0 +1,372 @@
|
|||||||
|
{
|
||||||
|
"~:features": {
|
||||||
|
"~#set": [
|
||||||
|
"fdata/path-data",
|
||||||
|
"plugins/runtime",
|
||||||
|
"design-tokens/v1",
|
||||||
|
"layout/grid",
|
||||||
|
"styles/v2",
|
||||||
|
"fdata/pointer-map",
|
||||||
|
"fdata/objects-map",
|
||||||
|
"render-wasm/v1",
|
||||||
|
"components/v2",
|
||||||
|
"fdata/shape-data-type",
|
||||||
|
"text-editor-wasm/v1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:team-id": "~u1091e979-bbec-8194-8005-f7aa420b5660",
|
||||||
|
"~:permissions": {
|
||||||
|
"~:type": "~:membership",
|
||||||
|
"~:is-owner": true,
|
||||||
|
"~:is-admin": true,
|
||||||
|
"~:can-edit": true,
|
||||||
|
"~:can-read": true,
|
||||||
|
"~:is-logged": true
|
||||||
|
},
|
||||||
|
"~:has-media-trimmed": false,
|
||||||
|
"~:comment-thread-seqn": 0,
|
||||||
|
"~:name": "simple-text",
|
||||||
|
"~:revn": 7,
|
||||||
|
"~:modified-at": "~m1749629891313",
|
||||||
|
"~:vern": 0,
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c72",
|
||||||
|
"~:is-shared": false,
|
||||||
|
"~:migrations": {
|
||||||
|
"~#ordered-set": [
|
||||||
|
"legacy-2",
|
||||||
|
"legacy-3",
|
||||||
|
"legacy-5",
|
||||||
|
"legacy-6",
|
||||||
|
"legacy-7",
|
||||||
|
"legacy-8",
|
||||||
|
"legacy-9",
|
||||||
|
"legacy-10",
|
||||||
|
"legacy-11",
|
||||||
|
"legacy-12",
|
||||||
|
"legacy-13",
|
||||||
|
"legacy-14",
|
||||||
|
"legacy-16",
|
||||||
|
"legacy-17",
|
||||||
|
"legacy-18",
|
||||||
|
"legacy-19",
|
||||||
|
"legacy-25",
|
||||||
|
"legacy-26",
|
||||||
|
"legacy-27",
|
||||||
|
"legacy-28",
|
||||||
|
"legacy-29",
|
||||||
|
"legacy-31",
|
||||||
|
"legacy-32",
|
||||||
|
"legacy-33",
|
||||||
|
"legacy-34",
|
||||||
|
"legacy-36",
|
||||||
|
"legacy-37",
|
||||||
|
"legacy-38",
|
||||||
|
"legacy-39",
|
||||||
|
"legacy-40",
|
||||||
|
"legacy-41",
|
||||||
|
"legacy-42",
|
||||||
|
"legacy-43",
|
||||||
|
"legacy-44",
|
||||||
|
"legacy-45",
|
||||||
|
"legacy-46",
|
||||||
|
"legacy-47",
|
||||||
|
"legacy-48",
|
||||||
|
"legacy-49",
|
||||||
|
"legacy-50",
|
||||||
|
"legacy-51",
|
||||||
|
"legacy-52",
|
||||||
|
"legacy-53",
|
||||||
|
"legacy-54",
|
||||||
|
"legacy-55",
|
||||||
|
"legacy-56",
|
||||||
|
"legacy-57",
|
||||||
|
"legacy-59",
|
||||||
|
"legacy-62",
|
||||||
|
"legacy-65",
|
||||||
|
"legacy-66",
|
||||||
|
"legacy-67",
|
||||||
|
"0001-remove-tokens-from-groups",
|
||||||
|
"0002-normalize-bool-content",
|
||||||
|
"0002-clean-shape-interactions",
|
||||||
|
"0003-fix-root-shape",
|
||||||
|
"0003-convert-path-content",
|
||||||
|
"0004-clean-shadow-and-colors",
|
||||||
|
"0005-deprecate-image-type",
|
||||||
|
"0006-fix-old-texts-fills",
|
||||||
|
"0007-clear-invalid-strokes-and-fills-v2",
|
||||||
|
"0008-fix-library-colors-opacity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:version": 67,
|
||||||
|
"~:project-id": "~u1091e979-bbec-8194-8005-f7aa420b8b07",
|
||||||
|
"~:created-at": "~m1749629823499",
|
||||||
|
"~:data": {
|
||||||
|
"~:pages": [
|
||||||
|
"~u3b0d758a-8c9d-8013-8006-52c8337e5c73"
|
||||||
|
],
|
||||||
|
"~:pages-index": {
|
||||||
|
"~u3b0d758a-8c9d-8013-8006-52c8337e5c73": {
|
||||||
|
"~:objects": {
|
||||||
|
"~u00000000-0000-0000-0000-000000000000": {
|
||||||
|
"~#shape": {
|
||||||
|
"~:y": 0,
|
||||||
|
"~:hide-fill-on-export": false,
|
||||||
|
"~:transform": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:rotation": 0,
|
||||||
|
"~:name": "Root Frame",
|
||||||
|
"~:width": 0.01,
|
||||||
|
"~:type": "~:frame",
|
||||||
|
"~:points": [
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.0,
|
||||||
|
"~:y": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.01,
|
||||||
|
"~:y": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.01,
|
||||||
|
"~:y": 0.01
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.0,
|
||||||
|
"~:y": 0.01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:r2": 0,
|
||||||
|
"~:proportion-lock": false,
|
||||||
|
"~:transform-inverse": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:r3": 0,
|
||||||
|
"~:r1": 0,
|
||||||
|
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:strokes": [],
|
||||||
|
"~:x": 0,
|
||||||
|
"~:proportion": 1.0,
|
||||||
|
"~:r4": 0,
|
||||||
|
"~:selrect": {
|
||||||
|
"~#rect": {
|
||||||
|
"~:x": 0,
|
||||||
|
"~:y": 0,
|
||||||
|
"~:width": 0.01,
|
||||||
|
"~:height": 0.01,
|
||||||
|
"~:x1": 0,
|
||||||
|
"~:y1": 0,
|
||||||
|
"~:x2": 0.01,
|
||||||
|
"~:y2": 0.01
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#FFFFFF",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:flip-x": null,
|
||||||
|
"~:height": 0.01,
|
||||||
|
"~:flip-y": null,
|
||||||
|
"~:shapes": [
|
||||||
|
"~u7274a6af-66db-8009-8006-52c837bed25d"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~u7274a6af-66db-8009-8006-52c837bed25d": {
|
||||||
|
"~#shape": {
|
||||||
|
"~:y": 368.000005463652,
|
||||||
|
"~:transform": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:rotation": 0,
|
||||||
|
"~:grow-type": "~:auto-width",
|
||||||
|
"~:content": {
|
||||||
|
"~:type": "root",
|
||||||
|
"~:key": "13hr3ftth2o",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:type": "paragraph-set",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "normal",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "normal",
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:font-id": "sourcesanspro",
|
||||||
|
"~:key": "1qm8gi1rphc",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "400",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:font-variant-id": "regular",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "sourcesanspro",
|
||||||
|
"~:text": "Hello "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "normal",
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:font-id": "gfont-sora",
|
||||||
|
"~:key": "2qm8gi1rphd",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "400",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:font-variant-id": "regular",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "Sora",
|
||||||
|
"~:text": "World"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:text-align": "left",
|
||||||
|
"~:font-id": "sourcesanspro",
|
||||||
|
"~:key": "r8gahivbg7",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "400",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:text-direction": "ltr",
|
||||||
|
"~:type": "paragraph",
|
||||||
|
"~:font-variant-id": "regular",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "sourcesanspro"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:vertical-align": "top"
|
||||||
|
},
|
||||||
|
"~:hide-in-viewer": false,
|
||||||
|
"~:name": "this is a text",
|
||||||
|
"~:width": 237.0000390021974,
|
||||||
|
"~:type": "~:text",
|
||||||
|
"~:points": [
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 414.9999714372273,
|
||||||
|
"~:y": 368.000005463652
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 652.0000104394247,
|
||||||
|
"~:y": 368.000005463652
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 652.0000104394247,
|
||||||
|
"~:y": 426.0000039162686
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 414.9999714372273,
|
||||||
|
"~:y": 426.0000039162686
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:layout-item-h-sizing": "~:fix",
|
||||||
|
"~:transform-inverse": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:layout-item-v-sizing": "~:fix",
|
||||||
|
"~:id": "~u7274a6af-66db-8009-8006-52c837bed25d",
|
||||||
|
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:x": 414.9999714372274,
|
||||||
|
"~:selrect": {
|
||||||
|
"~#rect": {
|
||||||
|
"~:x": 414.9999714372274,
|
||||||
|
"~:y": 368.000005463652,
|
||||||
|
"~:width": 237.0000390021974,
|
||||||
|
"~:height": 57.99999845261664,
|
||||||
|
"~:x1": 414.9999714372274,
|
||||||
|
"~:y1": 368.000005463652,
|
||||||
|
"~:x2": 652.0000104394248,
|
||||||
|
"~:y2": 426.0000039162686
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:flip-x": null,
|
||||||
|
"~:height": 57.99999845261664,
|
||||||
|
"~:flip-y": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c73",
|
||||||
|
"~:name": "Page 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c72",
|
||||||
|
"~:options": {
|
||||||
|
"~:components-v2": true,
|
||||||
|
"~:base-font-size": "16px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,372 @@
|
|||||||
|
{
|
||||||
|
"~:features": {
|
||||||
|
"~#set": [
|
||||||
|
"fdata/path-data",
|
||||||
|
"plugins/runtime",
|
||||||
|
"design-tokens/v1",
|
||||||
|
"layout/grid",
|
||||||
|
"styles/v2",
|
||||||
|
"fdata/pointer-map",
|
||||||
|
"fdata/objects-map",
|
||||||
|
"render-wasm/v1",
|
||||||
|
"components/v2",
|
||||||
|
"fdata/shape-data-type",
|
||||||
|
"text-editor-wasm/v1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:team-id": "~u1091e979-bbec-8194-8005-f7aa420b5660",
|
||||||
|
"~:permissions": {
|
||||||
|
"~:type": "~:membership",
|
||||||
|
"~:is-owner": true,
|
||||||
|
"~:is-admin": true,
|
||||||
|
"~:can-edit": true,
|
||||||
|
"~:can-read": true,
|
||||||
|
"~:is-logged": true
|
||||||
|
},
|
||||||
|
"~:has-media-trimmed": false,
|
||||||
|
"~:comment-thread-seqn": 0,
|
||||||
|
"~:name": "simple-text",
|
||||||
|
"~:revn": 7,
|
||||||
|
"~:modified-at": "~m1749629891313",
|
||||||
|
"~:vern": 0,
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c72",
|
||||||
|
"~:is-shared": false,
|
||||||
|
"~:migrations": {
|
||||||
|
"~#ordered-set": [
|
||||||
|
"legacy-2",
|
||||||
|
"legacy-3",
|
||||||
|
"legacy-5",
|
||||||
|
"legacy-6",
|
||||||
|
"legacy-7",
|
||||||
|
"legacy-8",
|
||||||
|
"legacy-9",
|
||||||
|
"legacy-10",
|
||||||
|
"legacy-11",
|
||||||
|
"legacy-12",
|
||||||
|
"legacy-13",
|
||||||
|
"legacy-14",
|
||||||
|
"legacy-16",
|
||||||
|
"legacy-17",
|
||||||
|
"legacy-18",
|
||||||
|
"legacy-19",
|
||||||
|
"legacy-25",
|
||||||
|
"legacy-26",
|
||||||
|
"legacy-27",
|
||||||
|
"legacy-28",
|
||||||
|
"legacy-29",
|
||||||
|
"legacy-31",
|
||||||
|
"legacy-32",
|
||||||
|
"legacy-33",
|
||||||
|
"legacy-34",
|
||||||
|
"legacy-36",
|
||||||
|
"legacy-37",
|
||||||
|
"legacy-38",
|
||||||
|
"legacy-39",
|
||||||
|
"legacy-40",
|
||||||
|
"legacy-41",
|
||||||
|
"legacy-42",
|
||||||
|
"legacy-43",
|
||||||
|
"legacy-44",
|
||||||
|
"legacy-45",
|
||||||
|
"legacy-46",
|
||||||
|
"legacy-47",
|
||||||
|
"legacy-48",
|
||||||
|
"legacy-49",
|
||||||
|
"legacy-50",
|
||||||
|
"legacy-51",
|
||||||
|
"legacy-52",
|
||||||
|
"legacy-53",
|
||||||
|
"legacy-54",
|
||||||
|
"legacy-55",
|
||||||
|
"legacy-56",
|
||||||
|
"legacy-57",
|
||||||
|
"legacy-59",
|
||||||
|
"legacy-62",
|
||||||
|
"legacy-65",
|
||||||
|
"legacy-66",
|
||||||
|
"legacy-67",
|
||||||
|
"0001-remove-tokens-from-groups",
|
||||||
|
"0002-normalize-bool-content",
|
||||||
|
"0002-clean-shape-interactions",
|
||||||
|
"0003-fix-root-shape",
|
||||||
|
"0003-convert-path-content",
|
||||||
|
"0004-clean-shadow-and-colors",
|
||||||
|
"0005-deprecate-image-type",
|
||||||
|
"0006-fix-old-texts-fills",
|
||||||
|
"0007-clear-invalid-strokes-and-fills-v2",
|
||||||
|
"0008-fix-library-colors-opacity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:version": 67,
|
||||||
|
"~:project-id": "~u1091e979-bbec-8194-8005-f7aa420b8b07",
|
||||||
|
"~:created-at": "~m1749629823499",
|
||||||
|
"~:data": {
|
||||||
|
"~:pages": [
|
||||||
|
"~u3b0d758a-8c9d-8013-8006-52c8337e5c73"
|
||||||
|
],
|
||||||
|
"~:pages-index": {
|
||||||
|
"~u3b0d758a-8c9d-8013-8006-52c8337e5c73": {
|
||||||
|
"~:objects": {
|
||||||
|
"~u00000000-0000-0000-0000-000000000000": {
|
||||||
|
"~#shape": {
|
||||||
|
"~:y": 0,
|
||||||
|
"~:hide-fill-on-export": false,
|
||||||
|
"~:transform": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:rotation": 0,
|
||||||
|
"~:name": "Root Frame",
|
||||||
|
"~:width": 0.01,
|
||||||
|
"~:type": "~:frame",
|
||||||
|
"~:points": [
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.0,
|
||||||
|
"~:y": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.01,
|
||||||
|
"~:y": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.01,
|
||||||
|
"~:y": 0.01
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 0.0,
|
||||||
|
"~:y": 0.01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:r2": 0,
|
||||||
|
"~:proportion-lock": false,
|
||||||
|
"~:transform-inverse": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:r3": 0,
|
||||||
|
"~:r1": 0,
|
||||||
|
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:strokes": [],
|
||||||
|
"~:x": 0,
|
||||||
|
"~:proportion": 1.0,
|
||||||
|
"~:r4": 0,
|
||||||
|
"~:selrect": {
|
||||||
|
"~#rect": {
|
||||||
|
"~:x": 0,
|
||||||
|
"~:y": 0,
|
||||||
|
"~:width": 0.01,
|
||||||
|
"~:height": 0.01,
|
||||||
|
"~:x1": 0,
|
||||||
|
"~:y1": 0,
|
||||||
|
"~:x2": 0.01,
|
||||||
|
"~:y2": 0.01
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#FFFFFF",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:flip-x": null,
|
||||||
|
"~:height": 0.01,
|
||||||
|
"~:flip-y": null,
|
||||||
|
"~:shapes": [
|
||||||
|
"~u7274a6af-66db-8009-8006-52c837bed25d"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~u7274a6af-66db-8009-8006-52c837bed25d": {
|
||||||
|
"~#shape": {
|
||||||
|
"~:y": 368.000005463652,
|
||||||
|
"~:transform": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:rotation": 0,
|
||||||
|
"~:grow-type": "~:auto-width",
|
||||||
|
"~:content": {
|
||||||
|
"~:type": "root",
|
||||||
|
"~:key": "13hr3ftth2o",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:type": "paragraph-set",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "normal",
|
||||||
|
"~:children": [
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "normal",
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:font-id": "sourcesanspro",
|
||||||
|
"~:key": "1qm8gi1rphc",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "400",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:font-variant-id": "regular",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "sourcesanspro",
|
||||||
|
"~:text": "Hello "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~:line-height": "1.2",
|
||||||
|
"~:font-style": "italic",
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:font-id": "sourcesanspro",
|
||||||
|
"~:key": "2qm8gi1rphd",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "600",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:font-variant-id": "600italic",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "sourcesanspro",
|
||||||
|
"~:text": "World"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:typography-ref-id": null,
|
||||||
|
"~:text-transform": "none",
|
||||||
|
"~:text-align": "left",
|
||||||
|
"~:font-id": "sourcesanspro",
|
||||||
|
"~:key": "r8gahivbg7",
|
||||||
|
"~:font-size": "48",
|
||||||
|
"~:font-weight": "400",
|
||||||
|
"~:typography-ref-file": null,
|
||||||
|
"~:text-direction": "ltr",
|
||||||
|
"~:type": "paragraph",
|
||||||
|
"~:font-variant-id": "regular",
|
||||||
|
"~:text-decoration": "none",
|
||||||
|
"~:letter-spacing": "0",
|
||||||
|
"~:fills": [
|
||||||
|
{
|
||||||
|
"~:fill-color": "#000000",
|
||||||
|
"~:fill-opacity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:font-family": "sourcesanspro"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:vertical-align": "top"
|
||||||
|
},
|
||||||
|
"~:hide-in-viewer": false,
|
||||||
|
"~:name": "this is a text",
|
||||||
|
"~:width": 237.0000390021974,
|
||||||
|
"~:type": "~:text",
|
||||||
|
"~:points": [
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 414.9999714372273,
|
||||||
|
"~:y": 368.000005463652
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 652.0000104394247,
|
||||||
|
"~:y": 368.000005463652
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 652.0000104394247,
|
||||||
|
"~:y": 426.0000039162686
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~#point": {
|
||||||
|
"~:x": 414.9999714372273,
|
||||||
|
"~:y": 426.0000039162686
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"~:layout-item-h-sizing": "~:fix",
|
||||||
|
"~:transform-inverse": {
|
||||||
|
"~#matrix": {
|
||||||
|
"~:a": 1.0,
|
||||||
|
"~:b": 0.0,
|
||||||
|
"~:c": 0.0,
|
||||||
|
"~:d": 1.0,
|
||||||
|
"~:e": 0.0,
|
||||||
|
"~:f": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:layout-item-v-sizing": "~:fix",
|
||||||
|
"~:id": "~u7274a6af-66db-8009-8006-52c837bed25d",
|
||||||
|
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||||
|
"~:x": 414.9999714372274,
|
||||||
|
"~:selrect": {
|
||||||
|
"~#rect": {
|
||||||
|
"~:x": 414.9999714372274,
|
||||||
|
"~:y": 368.000005463652,
|
||||||
|
"~:width": 237.0000390021974,
|
||||||
|
"~:height": 57.99999845261664,
|
||||||
|
"~:x1": 414.9999714372274,
|
||||||
|
"~:y1": 368.000005463652,
|
||||||
|
"~:x2": 652.0000104394248,
|
||||||
|
"~:y2": 426.0000039162686
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:flip-x": null,
|
||||||
|
"~:height": 57.99999845261664,
|
||||||
|
"~:flip-y": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c73",
|
||||||
|
"~:name": "Page 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u3b0d758a-8c9d-8013-8006-52c8337e5c72",
|
||||||
|
"~:options": {
|
||||||
|
"~:components-v2": true,
|
||||||
|
"~:base-font-size": "16px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
frontend/playwright/ui/specs/text-editor-v3.spec.js
Normal file
69
frontend/playwright/ui/specs/text-editor-v3.spec.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||||
|
|
||||||
|
const FILE = {
|
||||||
|
id: "3b0d758a-8c9d-8013-8006-52c8337e5c72",
|
||||||
|
pageId: "3b0d758a-8c9d-8013-8006-52c8337e5c73",
|
||||||
|
};
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await WasmWorkspacePage.init(page);
|
||||||
|
// WASM_FLAGS already enables render-wasm; add the WASM text editor on top.
|
||||||
|
await WasmWorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-wasm"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function openEditorAndSelectAll(workspace) {
|
||||||
|
await workspace.clickLeafLayer("this is a text");
|
||||||
|
// Enter edit mode (waits until the typography controls are ready) and then
|
||||||
|
// select every character so the sidebar reflects the combined styles of the
|
||||||
|
// whole text via the WASM editor path.
|
||||||
|
await workspace.textEditor.startEditing();
|
||||||
|
await workspace.page.keyboard.press("ControlOrMeta+a");
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe("BUG 10502 - Mixed families and variants", () => {
|
||||||
|
test("Multiple variants of the same font family", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page, { textEditor: true });
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("text-editor/get-file-10502-mixed-variants.json");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace(FILE);
|
||||||
|
await workspace.waitForFirstRender();
|
||||||
|
|
||||||
|
await openEditorAndSelectAll(workspace);
|
||||||
|
|
||||||
|
// The whole selection shares a single font family, so it must be shown even
|
||||||
|
// though the variants differ.
|
||||||
|
const fontFamily = workspace.rightSidebar.getByTitle("Font Family");
|
||||||
|
await expect(fontFamily).toContainText("Source Sans Pro");
|
||||||
|
|
||||||
|
// The variants differ across the selection, so the variant dropdown shows the
|
||||||
|
// "mixed" placeholder.
|
||||||
|
const fontVariant = workspace.rightSidebar
|
||||||
|
.getByTitle("Font Style")
|
||||||
|
.getByRole("combobox");
|
||||||
|
await expect(fontVariant).toHaveText("--");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Mixed font families appear as such in the dropdown", async ({ page }) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page, { textEditor: true });
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("text-editor/get-file-10502-mixed-families.json");
|
||||||
|
// Serve a stand-in TTF for Sora so the render doesn't wait on a real fetch.
|
||||||
|
// Glyphs are irrelevant here: the assertion only inspects the sidebar.
|
||||||
|
await workspace.mockGoogleFont("sora", "render-wasm/assets/ebgaramond.ttf");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace(FILE);
|
||||||
|
await workspace.waitForFirstRender();
|
||||||
|
|
||||||
|
await openEditorAndSelectAll(workspace);
|
||||||
|
|
||||||
|
// The selection mixes two different font families (Source Sans Pro and Sora),
|
||||||
|
// so the font family dropdown reports it as mixed.
|
||||||
|
const fontFamily = workspace.rightSidebar.getByTitle("Font Family");
|
||||||
|
await expect(fontFamily).toContainText("Mixed Font Families");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@ -360,6 +360,9 @@
|
|||||||
{:value (:id variant)
|
{:value (:id variant)
|
||||||
:key (pr-str variant)
|
:key (pr-str variant)
|
||||||
:label (:name variant)})))
|
:label (:name variant)})))
|
||||||
|
;; When the selection mixes variants we prepend a "--" entry: it is
|
||||||
|
;; shown as the collapsed value (nothing single is selected) while
|
||||||
|
;; the real variants of the resolved font are still listed below it.
|
||||||
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
|
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
|
||||||
(conj basic-variant-options
|
(conj basic-variant-options
|
||||||
{:value ""
|
{:value ""
|
||||||
|
|||||||
@ -286,6 +286,13 @@
|
|||||||
default (unchecked-get values "parent")]
|
default (unchecked-get values "parent")]
|
||||||
(d/nilv (unchecked-get values (d/name kind)) default)))
|
(d/nilv (unchecked-get values (d/name kind)) default)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn translate-multiple-state
|
||||||
|
[multiple-state]
|
||||||
|
(let [values (unchecked-get wasm/serializers "multiple-state")
|
||||||
|
default (unchecked-get values "undefined")]
|
||||||
|
(d/nilv (unchecked-get values (d/name multiple-state)) default)))
|
||||||
|
|
||||||
;; --- Guides
|
;; --- Guides
|
||||||
|
|
||||||
;; Each guide is serialized as 5 x 32-bit words:
|
;; Each guide is serialized as 5 x 32-bit words:
|
||||||
|
|||||||
@ -13,8 +13,11 @@
|
|||||||
[app.render-wasm.api.fonts :as fonts]
|
[app.render-wasm.api.fonts :as fonts]
|
||||||
[app.render-wasm.helpers :as h]
|
[app.render-wasm.helpers :as h]
|
||||||
[app.render-wasm.mem :as mem]
|
[app.render-wasm.mem :as mem]
|
||||||
|
[app.render-wasm.serializers :as sr]
|
||||||
[app.render-wasm.wasm :as wasm]))
|
[app.render-wasm.wasm :as wasm]))
|
||||||
|
|
||||||
|
(def multiple-state-multiple (sr/translate-multiple-state :multiple))
|
||||||
|
|
||||||
(def ^:const TEXT_EDITOR_STYLES_METADATA_SIZE (* 31 4))
|
(def ^:const TEXT_EDITOR_STYLES_METADATA_SIZE (* 31 4))
|
||||||
(def ^:const TEXT_EDITOR_STYLES_FILL_SOLID 0)
|
(def ^:const TEXT_EDITOR_STYLES_FILL_SOLID 0)
|
||||||
(def ^:const TEXT_EDITOR_STYLES_FILL_LINEAR_GRADIENT 1)
|
(def ^:const TEXT_EDITOR_STYLES_FILL_LINEAR_GRADIENT 1)
|
||||||
@ -260,33 +263,41 @@
|
|||||||
text-direction-state (aget heap-u32 (+ u32-offset 2))
|
text-direction-state (aget heap-u32 (+ u32-offset 2))
|
||||||
text-decoration-state (aget heap-u32 (+ u32-offset 3))
|
text-decoration-state (aget heap-u32 (+ u32-offset 3))
|
||||||
text-transform-state (aget heap-u32 (+ u32-offset 4))
|
text-transform-state (aget heap-u32 (+ u32-offset 4))
|
||||||
font-family-state (aget heap-u32 (+ u32-offset 5))
|
font-family-id-state (aget heap-u32 (+ u32-offset 5))
|
||||||
font-size-state (aget heap-u32 (+ u32-offset 6))
|
font-size-state (aget heap-u32 (+ u32-offset 6))
|
||||||
font-weight-state (aget heap-u32 (+ u32-offset 7))
|
font-weight-state (aget heap-u32 (+ u32-offset 7))
|
||||||
font-variant-id-state (aget heap-u32 (+ u32-offset 8))
|
;; Unused: the variant id is stored as a zero uuid for every span
|
||||||
|
_font-variant-id-state (aget heap-u32 (+ u32-offset 8))
|
||||||
line-height-state (aget heap-u32 (+ u32-offset 9))
|
line-height-state (aget heap-u32 (+ u32-offset 9))
|
||||||
letter-spacing-state (aget heap-u32 (+ u32-offset 10))
|
letter-spacing-state (aget heap-u32 (+ u32-offset 10))
|
||||||
num-fills (aget heap-u32 (+ u32-offset 11))
|
font-style-state (aget heap-u32 (+ u32-offset 11))
|
||||||
multiple-fills (aget heap-u32 (+ u32-offset 12))
|
num-fills (aget heap-u32 (+ u32-offset 12))
|
||||||
|
multiple-fills (aget heap-u32 (+ u32-offset 13))
|
||||||
|
|
||||||
text-align-value (aget heap-u32 (+ u32-offset 13))
|
text-align-value (aget heap-u32 (+ u32-offset 14))
|
||||||
text-direction-value (aget heap-u32 (+ u32-offset 14))
|
text-direction-value (aget heap-u32 (+ u32-offset 15))
|
||||||
text-decoration-value (aget heap-u32 (+ u32-offset 15))
|
text-decoration-value (aget heap-u32 (+ u32-offset 16))
|
||||||
text-transform-value (aget heap-u32 (+ u32-offset 16))
|
text-transform-value (aget heap-u32 (+ u32-offset 17))
|
||||||
font-family-id-a (aget heap-u32 (+ u32-offset 17))
|
font-family-id-a (aget heap-u32 (+ u32-offset 18))
|
||||||
font-family-id-b (aget heap-u32 (+ u32-offset 18))
|
font-family-id-b (aget heap-u32 (+ u32-offset 19))
|
||||||
font-family-id-c (aget heap-u32 (+ u32-offset 19))
|
font-family-id-c (aget heap-u32 (+ u32-offset 20))
|
||||||
font-family-id-d (aget heap-u32 (+ u32-offset 20))
|
font-family-id-d (aget heap-u32 (+ u32-offset 21))
|
||||||
font-family-id-value (uuid/from-unsigned-parts font-family-id-a font-family-id-b font-family-id-c font-family-id-d)
|
font-family-id-value (uuid/from-unsigned-parts font-family-id-a font-family-id-b font-family-id-c font-family-id-d)
|
||||||
font-family-style-value (aget heap-u32 (+ u32-offset 21))
|
font-style-raw-value (aget heap-u32 (+ u32-offset 22))
|
||||||
_font-family-weight-value (aget heap-u32 (+ u32-offset 22))
|
|
||||||
font-size-value (aget heap-f32 (+ u32-offset 23))
|
font-size-value (aget heap-f32 (+ u32-offset 23))
|
||||||
font-weight-value (aget heap-i32 (+ u32-offset 24))
|
font-weight-value (aget heap-i32 (+ u32-offset 24))
|
||||||
line-height-value (aget heap-f32 (+ u32-offset 29))
|
line-height-value (aget heap-f32 (+ u32-offset 29))
|
||||||
letter-spacing-value (aget heap-f32 (+ u32-offset 30))
|
letter-spacing-value (aget heap-f32 (+ u32-offset 30))
|
||||||
font-id (fonts/uuid->font-id font-family-id-value)
|
font-id (fonts/uuid->font-id font-family-id-value)
|
||||||
font-style-value (text-editor-translate-font-style (text-editor-get-style-property font-family-state font-family-style-value))
|
font-style-value (text-editor-translate-font-style (text-editor-get-style-property font-style-state font-style-raw-value))
|
||||||
font-variant-id-computed (text-editor-compute-font-variant-id font-id font-weight-value font-style-value)
|
font-variant-id-computed (text-editor-compute-font-variant-id font-id font-weight-value font-style-value)
|
||||||
|
;; A font variant is defined by its family + weight + style, so it
|
||||||
|
;; is "mixed" when any of those is mixed. When the family itself is
|
||||||
|
;; mixed there is no single font to resolve variants against, so we
|
||||||
|
;; also report the variant as mixed.
|
||||||
|
font-variant-multiple? (or (= font-family-id-state multiple-state-multiple)
|
||||||
|
(= font-weight-state multiple-state-multiple)
|
||||||
|
(= font-style-state multiple-state-multiple))
|
||||||
|
|
||||||
fills (->> (range num-fills)
|
fills (->> (range num-fills)
|
||||||
(map (fn [idx]
|
(map (fn [idx]
|
||||||
@ -313,9 +324,9 @@
|
|||||||
:font-size (text-editor-get-style-property font-size-state font-size-value)
|
:font-size (text-editor-get-style-property font-size-state font-size-value)
|
||||||
:font-weight (text-editor-get-style-property font-weight-state font-weight-value)
|
:font-weight (text-editor-get-style-property font-weight-state font-weight-value)
|
||||||
:font-style font-style-value
|
:font-style font-style-value
|
||||||
:font-family (text-editor-get-style-property font-family-state font-id)
|
:font-family (text-editor-get-style-property font-family-id-state font-id)
|
||||||
:font-id (text-editor-get-style-property font-family-state font-id)
|
:font-id (text-editor-get-style-property font-family-id-state font-id)
|
||||||
:font-variant-id (text-editor-get-style-property font-variant-id-state font-variant-id-computed)
|
:font-variant-id (if font-variant-multiple? :multiple font-variant-id-computed)
|
||||||
:typography-ref-file nil
|
:typography-ref-file nil
|
||||||
:typography-ref-id nil
|
:typography-ref-id nil
|
||||||
:selected-colors selected-colors
|
:selected-colors selected-colors
|
||||||
|
|||||||
@ -75,6 +75,7 @@
|
|||||||
:text-direction shared/RawTextDirection
|
:text-direction shared/RawTextDirection
|
||||||
:text-decoration shared/RawTextDecoration
|
:text-decoration shared/RawTextDecoration
|
||||||
:text-transform shared/RawTextTransform
|
:text-transform shared/RawTextTransform
|
||||||
|
:multiple-state shared/MultipleState
|
||||||
:transform-entry-kind shared/RawTransformEntryKind
|
:transform-entry-kind shared/RawTransformEntryKind
|
||||||
:segment-data shared/RawSegmentData
|
:segment-data shared/RawSegmentData
|
||||||
:stroke-linecap shared/RawStrokeLineCap
|
:stroke-linecap shared/RawStrokeLineCap
|
||||||
|
|||||||
@ -38,9 +38,12 @@ impl FontFamily {
|
|||||||
pub fn id(&self) -> Uuid {
|
pub fn id(&self) -> Uuid {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(&self) -> FontStyle {
|
pub fn style(&self) -> FontStyle {
|
||||||
self.style
|
self.style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn weight(&self) -> u32 {
|
pub fn weight(&self) -> u32 {
|
||||||
self.weight
|
self.weight
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
use macros::ToJs;
|
use macros::ToJs;
|
||||||
|
|
||||||
use crate::shapes::{
|
use crate::shapes::{
|
||||||
Fill, FontFamily, TextAlign, TextContent, TextDecoration, TextDirection,
|
Fill, FontStyle, TextAlign, TextContent, TextDecoration, TextDirection,
|
||||||
TextPositionWithAffinity, TextTransform, VerticalAlign,
|
TextPositionWithAffinity, TextTransform, VerticalAlign,
|
||||||
};
|
};
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
@ -88,6 +88,7 @@ impl TextSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Events that the text editor can emit for frontend synchronization
|
/// Events that the text editor can emit for frontend synchronization
|
||||||
|
/// FIXME: the serialization should be in the wasm module
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, ToJs)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ToJs)]
|
||||||
pub enum TextEditorEvent {
|
pub enum TextEditorEvent {
|
||||||
@ -111,7 +112,12 @@ pub struct TextEditorStyles {
|
|||||||
pub text_direction: Multiple<TextDirection>, // Multiple
|
pub text_direction: Multiple<TextDirection>, // Multiple
|
||||||
pub text_decoration: Multiple<TextDecoration>,
|
pub text_decoration: Multiple<TextDecoration>,
|
||||||
pub text_transform: Multiple<TextTransform>,
|
pub text_transform: Multiple<TextTransform>,
|
||||||
pub font_family: Multiple<FontFamily>,
|
// The font family is decomposed into its independent parts so the family
|
||||||
|
// dropdown shows a single family even when the selection mixes variants of
|
||||||
|
// the same family (e.g. regular + bold italic). The id identifies the
|
||||||
|
// family; weight is tracked by `font_weight`; only the style is left here.
|
||||||
|
pub font_family_id: Multiple<Uuid>,
|
||||||
|
pub font_style: Multiple<FontStyle>,
|
||||||
pub font_size: Multiple<f32>,
|
pub font_size: Multiple<f32>,
|
||||||
pub font_weight: Multiple<i32>,
|
pub font_weight: Multiple<i32>,
|
||||||
pub font_variant_id: Multiple<Uuid>,
|
pub font_variant_id: Multiple<Uuid>,
|
||||||
@ -121,7 +127,8 @@ pub struct TextEditorStyles {
|
|||||||
pub fills: Vec<Fill>,
|
pub fills: Vec<Fill>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
// FIXME: the serialization should be in the wasm module
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, ToJs)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum MultipleState {
|
pub enum MultipleState {
|
||||||
Undefined = 0,
|
Undefined = 0,
|
||||||
@ -228,7 +235,8 @@ impl TextEditorStyles {
|
|||||||
text_direction: Multiple::empty(),
|
text_direction: Multiple::empty(),
|
||||||
text_decoration: Multiple::empty(),
|
text_decoration: Multiple::empty(),
|
||||||
text_transform: Multiple::empty(),
|
text_transform: Multiple::empty(),
|
||||||
font_family: Multiple::empty(),
|
font_family_id: Multiple::empty(),
|
||||||
|
font_style: Multiple::empty(),
|
||||||
font_size: Multiple::empty(),
|
font_size: Multiple::empty(),
|
||||||
font_weight: Multiple::empty(),
|
font_weight: Multiple::empty(),
|
||||||
font_variant_id: Multiple::empty(),
|
font_variant_id: Multiple::empty(),
|
||||||
@ -244,7 +252,8 @@ impl TextEditorStyles {
|
|||||||
self.text_direction.reset();
|
self.text_direction.reset();
|
||||||
self.text_decoration.reset();
|
self.text_decoration.reset();
|
||||||
self.text_transform.reset();
|
self.text_transform.reset();
|
||||||
self.font_family.reset();
|
self.font_family_id.reset();
|
||||||
|
self.font_style.reset();
|
||||||
self.font_size.reset();
|
self.font_size.reset();
|
||||||
self.font_weight.reset();
|
self.font_weight.reset();
|
||||||
self.font_variant_id.reset();
|
self.font_variant_id.reset();
|
||||||
@ -614,8 +623,11 @@ impl TextEditorState {
|
|||||||
.text_transform
|
.text_transform
|
||||||
.merge(span.text_transform);
|
.merge(span.text_transform);
|
||||||
self.current_styles
|
self.current_styles
|
||||||
.font_family
|
.font_family_id
|
||||||
.merge(Some(span.font_family));
|
.merge(Some(span.font_family.id()));
|
||||||
|
self.current_styles
|
||||||
|
.font_style
|
||||||
|
.merge(Some(span.font_family.style()));
|
||||||
self.current_styles.font_size.merge(Some(span.font_size));
|
self.current_styles.font_size.merge(Some(span.font_size));
|
||||||
self.current_styles
|
self.current_styles
|
||||||
.font_weight
|
.font_weight
|
||||||
@ -667,8 +679,11 @@ impl TextEditorState {
|
|||||||
.text_transform
|
.text_transform
|
||||||
.set_single(text_span.text_transform);
|
.set_single(text_span.text_transform);
|
||||||
self.current_styles
|
self.current_styles
|
||||||
.font_family
|
.font_family_id
|
||||||
.set_single(Some(text_span.font_family));
|
.set_single(Some(text_span.font_family.id()));
|
||||||
|
self.current_styles
|
||||||
|
.font_style
|
||||||
|
.set_single(Some(text_span.font_family.style()));
|
||||||
self.current_styles
|
self.current_styles
|
||||||
.font_size
|
.font_size
|
||||||
.set_single(Some(text_span.font_size));
|
.set_single(Some(text_span.font_size));
|
||||||
|
|||||||
@ -722,27 +722,20 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
|||||||
.unwrap_or(RawTextTransform::None as u32);
|
.unwrap_or(RawTextTransform::None as u32);
|
||||||
|
|
||||||
let font_family_id = styles
|
let font_family_id = styles
|
||||||
.font_family
|
.font_family_id
|
||||||
.value()
|
.value()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|value| {
|
.map(|value| {
|
||||||
let (a, b, c, d) = uuid_to_u32_quartet(&value.id());
|
let (a, b, c, d) = uuid_to_u32_quartet(value);
|
||||||
[a, b, c, d]
|
[a, b, c, d]
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let font_family_weight = styles
|
let font_style = styles
|
||||||
.font_family
|
.font_style
|
||||||
.value()
|
.value()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|value| value.weight())
|
.map(|value| *value as u32)
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let font_family_style = styles
|
|
||||||
.font_family
|
|
||||||
.value()
|
|
||||||
.as_ref()
|
|
||||||
.map(|value| value.style() as u32)
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let font_size = styles.font_size.value().unwrap_or(0.0);
|
let font_size = styles.font_size.value().unwrap_or(0.0);
|
||||||
@ -767,35 +760,35 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout: 48-byte fixed header + fixed values + serialized fills.
|
// Layout: 56-byte fixed header + fixed values + serialized fills.
|
||||||
let mut bytes = Vec::with_capacity(132 + fill_bytes.len());
|
let mut bytes = Vec::with_capacity(132 + fill_bytes.len());
|
||||||
|
|
||||||
// Header data // offset // index
|
// Header data (multiple-states) // offset // index
|
||||||
bytes.extend_from_slice(&vertical_align.to_le_bytes()); // 0 // 0
|
bytes.extend_from_slice(&vertical_align.to_le_bytes()); // 0 // 0
|
||||||
bytes.extend_from_slice(&(*styles.text_align.state() as u32).to_le_bytes()); // 4 // 1
|
bytes.extend_from_slice(&(*styles.text_align.state() as u32).to_le_bytes()); // 4 // 1
|
||||||
bytes.extend_from_slice(&(*styles.text_direction.state() as u32).to_le_bytes()); // 8 // 2
|
bytes.extend_from_slice(&(*styles.text_direction.state() as u32).to_le_bytes()); // 8 // 2
|
||||||
bytes.extend_from_slice(&(*styles.text_decoration.state() as u32).to_le_bytes()); // 12 // 3
|
bytes.extend_from_slice(&(*styles.text_decoration.state() as u32).to_le_bytes()); // 12 // 3
|
||||||
bytes.extend_from_slice(&(*styles.text_transform.state() as u32).to_le_bytes()); // 16 // 4
|
bytes.extend_from_slice(&(*styles.text_transform.state() as u32).to_le_bytes()); // 16 // 4
|
||||||
bytes.extend_from_slice(&(*styles.font_family.state() as u32).to_le_bytes()); // 20 // 5
|
bytes.extend_from_slice(&(*styles.font_family_id.state() as u32).to_le_bytes()); // 20 // 5
|
||||||
bytes.extend_from_slice(&(*styles.font_size.state() as u32).to_le_bytes()); // 24 // 6
|
bytes.extend_from_slice(&(*styles.font_size.state() as u32).to_le_bytes()); // 24 // 6
|
||||||
bytes.extend_from_slice(&(*styles.font_weight.state() as u32).to_le_bytes()); // 28 // 7
|
bytes.extend_from_slice(&(*styles.font_weight.state() as u32).to_le_bytes()); // 28 // 7
|
||||||
bytes.extend_from_slice(&(*styles.font_variant_id.state() as u32).to_le_bytes()); // 32 // 8
|
bytes.extend_from_slice(&(*styles.font_variant_id.state() as u32).to_le_bytes()); // 32 // 8
|
||||||
bytes.extend_from_slice(&(*styles.line_height.state() as u32).to_le_bytes()); // 36 // 9
|
bytes.extend_from_slice(&(*styles.line_height.state() as u32).to_le_bytes()); // 36 // 9
|
||||||
bytes.extend_from_slice(&(*styles.letter_spacing.state() as u32).to_le_bytes()); // 40 // 10
|
bytes.extend_from_slice(&(*styles.letter_spacing.state() as u32).to_le_bytes()); // 40 // 10
|
||||||
bytes.extend_from_slice(&fill_count.to_le_bytes()); // 44 // 11
|
bytes.extend_from_slice(&(*styles.font_style.state() as u32).to_le_bytes()); // 44 // 11
|
||||||
bytes.extend_from_slice(&(fill_multiple as u32).to_le_bytes()); // 48 // 12
|
bytes.extend_from_slice(&fill_count.to_le_bytes()); // 48 // 12
|
||||||
|
bytes.extend_from_slice(&(fill_multiple as u32).to_le_bytes()); // 52 // 13
|
||||||
|
|
||||||
// Value section.
|
// Value section.
|
||||||
bytes.extend_from_slice(&text_align.to_le_bytes()); // 52 // 13
|
bytes.extend_from_slice(&text_align.to_le_bytes()); // 56 // 14
|
||||||
bytes.extend_from_slice(&text_direction.to_le_bytes()); // 56 // 14
|
bytes.extend_from_slice(&text_direction.to_le_bytes()); // 60 // 15
|
||||||
bytes.extend_from_slice(&text_decoration.to_le_bytes()); // 60 // 15
|
bytes.extend_from_slice(&text_decoration.to_le_bytes()); // 64 // 16
|
||||||
bytes.extend_from_slice(&text_transform.to_le_bytes()); // 64 // 16
|
bytes.extend_from_slice(&text_transform.to_le_bytes()); // 68 // 17
|
||||||
bytes.extend_from_slice(&font_family_id[0].to_le_bytes()); // 68 // 17
|
bytes.extend_from_slice(&font_family_id[0].to_le_bytes()); // 72 // 18
|
||||||
bytes.extend_from_slice(&font_family_id[1].to_le_bytes()); // 72 // 18
|
bytes.extend_from_slice(&font_family_id[1].to_le_bytes()); // 76 // 19
|
||||||
bytes.extend_from_slice(&font_family_id[2].to_le_bytes()); // 76 // 19
|
bytes.extend_from_slice(&font_family_id[2].to_le_bytes()); // 80 // 20
|
||||||
bytes.extend_from_slice(&font_family_id[3].to_le_bytes()); // 80 // 20
|
bytes.extend_from_slice(&font_family_id[3].to_le_bytes()); // 84 // 21
|
||||||
bytes.extend_from_slice(&font_family_style.to_le_bytes()); // 84 // 21
|
bytes.extend_from_slice(&font_style.to_le_bytes()); // 88 // 22
|
||||||
bytes.extend_from_slice(&font_family_weight.to_le_bytes()); // 88 // 22
|
|
||||||
bytes.extend_from_slice(&font_size.to_le_bytes()); // 92 // 23
|
bytes.extend_from_slice(&font_size.to_le_bytes()); // 92 // 23
|
||||||
bytes.extend_from_slice(&font_weight.to_le_bytes()); // 96 // 24
|
bytes.extend_from_slice(&font_weight.to_le_bytes()); // 96 // 24
|
||||||
bytes.extend_from_slice(&font_variant_id[0].to_le_bytes()); // 100 // 25
|
bytes.extend_from_slice(&font_variant_id[0].to_le_bytes()); // 100 // 25
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user