mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
🐛 Fix mixed fills issues
This commit is contained in:
parent
77b4d07d1f
commit
424b689dca
@ -503,13 +503,9 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when (or
|
||||
(and (features/active-feature? state "text-editor-wasm/v1")
|
||||
(nil? (get-in state [:workspace-wasm-editor-styles id])))
|
||||
(and (features/active-feature? state "text-editor/v2")
|
||||
(not (features/active-feature? state "text-editor-wasm/v1"))
|
||||
(nil? (:workspace-editor state)))
|
||||
(and (not (features/active-feature? state "text-editor/v2"))
|
||||
(not (features/active-feature? state "text-editor-wasm/v1"))
|
||||
(nil? (get-in state [:workspace-editor-state id]))))
|
||||
(let [page-id (or (get options :page-id)
|
||||
(get state :current-page-id))
|
||||
@ -533,16 +529,20 @@
|
||||
(-> shape
|
||||
(dissoc :fills)
|
||||
(d/update-when :content update-content)))]
|
||||
(rx/of (dwsh/update-shapes shape-ids update-shape options)))))
|
||||
|
||||
(rx/concat (rx/of (dwsh/update-shapes shape-ids update-shape options))
|
||||
(when (features/active-feature? state "text-editor-wasm/v1")
|
||||
(let [styles ((comp update-node-fn migrate-node))
|
||||
result (wasm.api/apply-styles-to-selection styles)]
|
||||
(when result
|
||||
(rx/of (v2-update-text-shape-content
|
||||
(:shape-id result)
|
||||
(:content result)
|
||||
:update-name? true)))))))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(cond
|
||||
(features/active-feature? state "text-editor-wasm/v1")
|
||||
(let [styles ((comp update-node-fn migrate-node))]
|
||||
(wasm.api/apply-styles-to-selection styles))
|
||||
|
||||
(features/active-feature? state "text-editor/v2")
|
||||
(when (features/active-feature? state "text-editor/v2")
|
||||
(when-let [instance (:workspace-editor state)]
|
||||
(let [styles (some-> (editor.v2/getCurrentStyle instance)
|
||||
(styles/get-styles-from-style-declaration :removed-mixed true)
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
[app.render-wasm.mem :as mem]
|
||||
[app.render-wasm.wasm :as wasm]))
|
||||
|
||||
(def ^:const TEXT_EDITOR_STYLES_METADATA_SIZE (* 30 4))
|
||||
(def ^:const TEXT_EDITOR_STYLES_METADATA_SIZE (* 31 4))
|
||||
(def ^:const TEXT_EDITOR_STYLES_FILL_SOLID 0)
|
||||
(def ^:const TEXT_EDITOR_STYLES_FILL_LINEAR_GRADIENT 1)
|
||||
(def ^:const TEXT_EDITOR_STYLES_FILL_RADIAL_GRADIENT 2)
|
||||
@ -261,22 +261,23 @@
|
||||
line-height-state (aget heap-u32 (+ u32-offset 9))
|
||||
letter-spacing-state (aget heap-u32 (+ u32-offset 10))
|
||||
num-fills (aget heap-u32 (+ u32-offset 11))
|
||||
multiple-fills (aget heap-u32 (+ u32-offset 12))
|
||||
|
||||
text-align-value (aget heap-u32 (+ u32-offset 12))
|
||||
text-direction-value (aget heap-u32 (+ u32-offset 13))
|
||||
text-decoration-value (aget heap-u32 (+ u32-offset 14))
|
||||
text-transform-value (aget heap-u32 (+ u32-offset 15))
|
||||
font-family-id-a (aget heap-u32 (+ u32-offset 16))
|
||||
font-family-id-b (aget heap-u32 (+ u32-offset 17))
|
||||
font-family-id-c (aget heap-u32 (+ u32-offset 18))
|
||||
font-family-id-d (aget heap-u32 (+ u32-offset 19))
|
||||
text-align-value (aget heap-u32 (+ u32-offset 13))
|
||||
text-direction-value (aget heap-u32 (+ u32-offset 14))
|
||||
text-decoration-value (aget heap-u32 (+ u32-offset 15))
|
||||
text-transform-value (aget heap-u32 (+ u32-offset 16))
|
||||
font-family-id-a (aget heap-u32 (+ u32-offset 17))
|
||||
font-family-id-b (aget heap-u32 (+ u32-offset 18))
|
||||
font-family-id-c (aget heap-u32 (+ u32-offset 19))
|
||||
font-family-id-d (aget heap-u32 (+ u32-offset 20))
|
||||
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 20))
|
||||
_font-family-weight-value (aget heap-u32 (+ u32-offset 21))
|
||||
font-size-value (aget heap-f32 (+ u32-offset 22))
|
||||
font-weight-value (aget heap-i32 (+ u32-offset 23))
|
||||
line-height-value (aget heap-f32 (+ u32-offset 28))
|
||||
letter-spacing-value (aget heap-f32 (+ u32-offset 29))
|
||||
font-family-style-value (aget heap-u32 (+ u32-offset 21))
|
||||
_font-family-weight-value (aget heap-u32 (+ u32-offset 22))
|
||||
font-size-value (aget heap-f32 (+ u32-offset 23))
|
||||
font-weight-value (aget heap-i32 (+ u32-offset 24))
|
||||
line-height-value (aget heap-f32 (+ u32-offset 29))
|
||||
letter-spacing-value (aget heap-f32 (+ u32-offset 30))
|
||||
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-variant-id-computed (text-editor-compute-font-variant-id font-id font-weight-value font-style-value)
|
||||
@ -291,6 +292,11 @@
|
||||
(filter some?)
|
||||
(into []))
|
||||
|
||||
;; The order of these two variables is important, do not
|
||||
;; reorder them.
|
||||
selected-colors (if (= multiple-fills 1) fills nil)
|
||||
fills (if (= multiple-fills 1) :multiple fills)
|
||||
|
||||
result {:vertical-align (text-editor-translate-vertical-align vertical-align)
|
||||
:text-align (text-editor-translate-text-align (text-editor-get-style-property text-align-state text-align-value))
|
||||
:text-direction (text-editor-translate-text-direction (text-editor-get-style-property text-direction-state text-direction-value))
|
||||
@ -306,6 +312,7 @@
|
||||
:font-variant-id (text-editor-get-style-property font-variant-id-state font-variant-id-computed)
|
||||
:typography-ref-file nil
|
||||
:typography-ref-id nil
|
||||
:selected-colors selected-colors
|
||||
:fills fills}]
|
||||
|
||||
(mem/free)
|
||||
@ -471,6 +478,19 @@
|
||||
;; This is used as a intermediate cache between Clojure global state and WASM state.
|
||||
(def ^:private shape-text-contents (atom {}))
|
||||
|
||||
(defn cache-shape-text-content!
|
||||
[shape-id content]
|
||||
(when (some? content)
|
||||
(swap! shape-text-contents assoc shape-id content)))
|
||||
|
||||
(defn get-cached-content
|
||||
[shape-id]
|
||||
(get @shape-text-contents shape-id))
|
||||
|
||||
(defn update-cached-content!
|
||||
[shape-id content]
|
||||
(swap! shape-text-contents assoc shape-id content))
|
||||
|
||||
(defn- merge-exported-texts-into-content
|
||||
"Merge exported span texts back into the existing content tree.
|
||||
|
||||
@ -522,26 +542,13 @@
|
||||
new-texts (text-editor-export-content)]
|
||||
(when (and shape-id new-texts)
|
||||
(let [texts-clj (js->clj new-texts)
|
||||
content (get @shape-text-contents shape-id)]
|
||||
content (get-cached-content shape-id)]
|
||||
(when content
|
||||
(let [merged (merge-exported-texts-into-content content texts-clj)]
|
||||
(swap! shape-text-contents assoc shape-id merged)
|
||||
{:shape-id shape-id
|
||||
:content merged})))))))
|
||||
|
||||
(defn cache-shape-text-content!
|
||||
[shape-id content]
|
||||
(when (some? content)
|
||||
(swap! shape-text-contents assoc shape-id content)))
|
||||
|
||||
(defn get-cached-content
|
||||
[shape-id]
|
||||
(get @shape-text-contents shape-id))
|
||||
|
||||
(defn update-cached-content!
|
||||
[shape-id content]
|
||||
(swap! shape-text-contents assoc shape-id content))
|
||||
|
||||
(defn- normalize-selection
|
||||
"Given anchor/focus para+offset, return {:start-para :start-offset :end-para :end-offset}
|
||||
ordered so start <= end."
|
||||
@ -558,6 +565,7 @@
|
||||
Splits spans at boundaries as needed."
|
||||
[para sel-start sel-end attrs]
|
||||
(let [spans (:children para)
|
||||
|
||||
result (loop [spans spans
|
||||
pos 0
|
||||
acc []]
|
||||
@ -594,7 +602,7 @@
|
||||
selection (text-editor-get-selection)]
|
||||
|
||||
(when (and shape-id selection)
|
||||
(let [content (get @shape-text-contents shape-id)]
|
||||
(let [content (get-cached-content shape-id)]
|
||||
(when content
|
||||
(let [normalized-selection (normalize-selection selection)
|
||||
{:keys [start-para start-offset end-para end-offset]} normalized-selection
|
||||
@ -630,11 +638,13 @@
|
||||
|
||||
(range (count paragraphs))
|
||||
paragraphs))
|
||||
|
||||
new-content (when new-paragraphs
|
||||
(assoc content :children
|
||||
[(assoc paragraph-set :children new-paragraphs)]))]
|
||||
|
||||
(when new-content
|
||||
(swap! shape-text-contents assoc shape-id new-content)
|
||||
(update-cached-content! shape-id new-content)
|
||||
(use-shape-fn shape-id)
|
||||
(set-shape-text-content-fn shape-id new-content)
|
||||
{:shape-id shape-id
|
||||
|
||||
@ -117,6 +117,7 @@ pub struct TextEditorStyles {
|
||||
pub font_variant_id: Multiple<Uuid>,
|
||||
pub line_height: Multiple<f32>,
|
||||
pub letter_spacing: Multiple<f32>,
|
||||
pub fills_are_multiple: bool,
|
||||
pub fills: Vec<Fill>,
|
||||
}
|
||||
|
||||
@ -233,6 +234,7 @@ impl TextEditorStyles {
|
||||
font_variant_id: Multiple::empty(),
|
||||
line_height: Multiple::empty(),
|
||||
letter_spacing: Multiple::empty(),
|
||||
fills_are_multiple: false,
|
||||
fills: Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -248,6 +250,7 @@ impl TextEditorStyles {
|
||||
self.font_variant_id.reset();
|
||||
self.line_height.reset();
|
||||
self.letter_spacing.reset();
|
||||
self.fills_are_multiple = false;
|
||||
self.fills.clear();
|
||||
}
|
||||
}
|
||||
@ -529,11 +532,7 @@ impl TextEditorState {
|
||||
let end_paragraph = end.paragraph.min(paragraphs.len() - 1);
|
||||
|
||||
self.current_styles.reset();
|
||||
|
||||
let mut has_selected_content = false;
|
||||
let mut has_fills = false;
|
||||
let mut fills_are_multiple = false;
|
||||
|
||||
for (para_idx, paragraph) in paragraphs
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -606,14 +605,11 @@ impl TextEditorState {
|
||||
.letter_spacing
|
||||
.merge(Some(span.letter_spacing));
|
||||
|
||||
if !fills_are_multiple {
|
||||
if !has_fills {
|
||||
self.current_styles.fills = span.fills.clone();
|
||||
has_fills = true;
|
||||
} else if self.current_styles.fills != span.fills {
|
||||
fills_are_multiple = true;
|
||||
self.current_styles.fills.clear();
|
||||
}
|
||||
if self.current_styles.fills.is_empty() {
|
||||
self.current_styles.fills.append(&mut span.fills.clone());
|
||||
} else if self.current_styles.fills != span.fills {
|
||||
self.current_styles.fills_are_multiple = true;
|
||||
self.current_styles.fills.append(&mut span.fills.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -630,6 +626,7 @@ impl TextEditorState {
|
||||
let current_offset = focus.offset;
|
||||
let current_text_span = find_text_span_at_offset(current_paragraph, current_offset);
|
||||
|
||||
self.current_styles.reset();
|
||||
self.current_styles
|
||||
.text_align
|
||||
.set_single(Some(current_paragraph.text_align()));
|
||||
|
||||
@ -769,6 +769,7 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
||||
}
|
||||
|
||||
let mut fill_bytes = Vec::new();
|
||||
let fill_multiple = styles.fills_are_multiple;
|
||||
let mut fill_count: u32 = 0;
|
||||
for fill in &styles.fills {
|
||||
if let Ok(raw_fill) = RawFillData::try_from(fill) {
|
||||
@ -781,39 +782,41 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
||||
// Layout: 48-byte fixed header + fixed values + serialized fills.
|
||||
let mut bytes = Vec::with_capacity(132 + fill_bytes.len());
|
||||
|
||||
bytes.extend_from_slice(&vertical_align.to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.text_align.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.text_direction.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.text_decoration.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.text_transform.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.font_family.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.font_size.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.font_weight.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.font_variant_id.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.line_height.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&(*styles.letter_spacing.state() as u32).to_le_bytes());
|
||||
bytes.extend_from_slice(&fill_count.to_le_bytes());
|
||||
// Header data // offset // index
|
||||
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_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_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_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_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.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(&(fill_multiple as u32).to_le_bytes()); // 48 // 12
|
||||
|
||||
// Value section.
|
||||
bytes.extend_from_slice(&text_align.to_le_bytes());
|
||||
bytes.extend_from_slice(&text_direction.to_le_bytes());
|
||||
bytes.extend_from_slice(&text_decoration.to_le_bytes());
|
||||
bytes.extend_from_slice(&text_transform.to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_id[0].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_id[1].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_id[2].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_id[3].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_style.to_le_bytes());
|
||||
bytes.extend_from_slice(&font_family_weight.to_le_bytes());
|
||||
bytes.extend_from_slice(&font_size.to_le_bytes());
|
||||
bytes.extend_from_slice(&font_weight.to_le_bytes());
|
||||
bytes.extend_from_slice(&font_variant_id[0].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_variant_id[1].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_variant_id[2].to_le_bytes());
|
||||
bytes.extend_from_slice(&font_variant_id[3].to_le_bytes());
|
||||
bytes.extend_from_slice(&line_height.to_le_bytes());
|
||||
bytes.extend_from_slice(&letter_spacing.to_le_bytes());
|
||||
bytes.extend_from_slice(&fill_bytes);
|
||||
bytes.extend_from_slice(&text_align.to_le_bytes()); // 52 // 13
|
||||
bytes.extend_from_slice(&text_direction.to_le_bytes()); // 56 // 14
|
||||
bytes.extend_from_slice(&text_decoration.to_le_bytes()); // 60 // 15
|
||||
bytes.extend_from_slice(&text_transform.to_le_bytes()); // 64 // 16
|
||||
bytes.extend_from_slice(&font_family_id[0].to_le_bytes()); // 68 // 17
|
||||
bytes.extend_from_slice(&font_family_id[1].to_le_bytes()); // 72 // 18
|
||||
bytes.extend_from_slice(&font_family_id[2].to_le_bytes()); // 76 // 19
|
||||
bytes.extend_from_slice(&font_family_id[3].to_le_bytes()); // 80 // 20
|
||||
bytes.extend_from_slice(&font_family_style.to_le_bytes()); // 84 // 21
|
||||
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_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[1].to_le_bytes()); // 104 // 26
|
||||
bytes.extend_from_slice(&font_variant_id[2].to_le_bytes()); // 108 // 27
|
||||
bytes.extend_from_slice(&font_variant_id[3].to_le_bytes()); // 112 // 28
|
||||
bytes.extend_from_slice(&line_height.to_le_bytes()); // 116 // 29
|
||||
bytes.extend_from_slice(&letter_spacing.to_le_bytes()); // 120 // 30
|
||||
bytes.extend_from_slice(&fill_bytes); // 124
|
||||
|
||||
mem::write_bytes(bytes)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user