mirror of
https://github.com/penpot/penpot.git
synced 2026-09-12 23:19:28 +00:00
🎉 Add dash and gap inputs for dashed strokes (#9765)
* ✨ Add dash and gap customization for dashed strokes Signed-off-by: eureka0928 <meobius123@gmail.com> * ♻️ Change old numeric-inputs for new components --------- Signed-off-by: eureka0928 <meobius123@gmail.com> Signed-off-by: Andrey Antukh <niwi@niwi.nz> Co-authored-by: eureka0928 <meobius123@gmail.com> Co-authored-by: Andrey Antukh <niwi@niwi.nz> Co-authored-by: Alejandro Alonso <alejandro.alonso@kaleidos.net>
This commit is contained in:
parent
05fa8af479
commit
2c453e4a00
@ -137,6 +137,8 @@
|
|||||||
[:stroke-style {:optional true}
|
[:stroke-style {:optional true}
|
||||||
[::sm/one-of #{:solid :dotted :dashed :mixed}]]
|
[::sm/one-of #{:solid :dotted :dashed :mixed}]]
|
||||||
[:stroke-width {:optional true} ::sm/safe-number]
|
[:stroke-width {:optional true} ::sm/safe-number]
|
||||||
|
[:stroke-dash {:optional true} ::sm/safe-number]
|
||||||
|
[:stroke-gap {:optional true} ::sm/safe-number]
|
||||||
[:stroke-alignment {:optional true}
|
[:stroke-alignment {:optional true}
|
||||||
[::sm/one-of #{:center :inner :outer}]]
|
[::sm/one-of #{:center :inner :outer}]]
|
||||||
[:stroke-cap-start {:optional true}
|
[:stroke-cap-start {:optional true}
|
||||||
|
|||||||
@ -45,6 +45,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -151,6 +153,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -206,6 +210,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -261,6 +267,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -315,6 +323,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -433,6 +443,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -488,6 +500,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
|
|||||||
@ -19,14 +19,14 @@
|
|||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
(defn- calculate-dasharray
|
(defn- calculate-dasharray
|
||||||
[style width]
|
[style width dash gap]
|
||||||
(let [w+5 (+ 5 width)
|
(let [w+5 (+ 5 width)
|
||||||
w+1 (+ 1 width)
|
w+1 (+ 1 width)
|
||||||
w+10 (+ 10 width)]
|
w+10 (+ 10 width)]
|
||||||
(case style
|
(case style
|
||||||
:mixed (str/concat "" w+5 "," w+5 "," w+1 "," w+5)
|
:mixed (str/concat "" w+5 "," w+5 "," w+1 "," w+5)
|
||||||
:dotted (str/concat "0," w+5)
|
:dotted (str/concat "0," w+5)
|
||||||
:dashed (str/concat "" w+10 "," w+10)
|
:dashed (str/concat "" (or dash w+10) "," (or gap w+10))
|
||||||
"")))
|
"")))
|
||||||
|
|
||||||
(defn get-border-props
|
(defn get-border-props
|
||||||
@ -97,6 +97,8 @@
|
|||||||
(let [style (:stroke-style data :solid)]
|
(let [style (:stroke-style data :solid)]
|
||||||
(when-not (= style :none)
|
(when-not (= style :none)
|
||||||
(let [width (:stroke-width data 1)
|
(let [width (:stroke-width data 1)
|
||||||
|
dash (:stroke-dash data)
|
||||||
|
gap (:stroke-gap data)
|
||||||
gradient (:stroke-color-gradient data)
|
gradient (:stroke-color-gradient data)
|
||||||
color (:stroke-color data)
|
color (:stroke-color data)
|
||||||
opacity (:stroke-opacity data)]
|
opacity (:stroke-opacity data)]
|
||||||
@ -114,7 +116,7 @@
|
|||||||
(obj/set! attrs "strokeOpacity" opacity))
|
(obj/set! attrs "strokeOpacity" opacity))
|
||||||
|
|
||||||
(when (not= style :svg)
|
(when (not= style :svg)
|
||||||
(obj/set! attrs "strokeDasharray" (calculate-dasharray style width)))
|
(obj/set! attrs "strokeDasharray" (calculate-dasharray style width dash gap)))
|
||||||
|
|
||||||
;; For simple line caps we use svg stroke-line-cap attribute. This
|
;; For simple line caps we use svg stroke-line-cap attribute. This
|
||||||
;; only works if all caps are the same and we are not using the tricks
|
;; only works if all caps are the same and we are not using the tricks
|
||||||
|
|||||||
@ -333,6 +333,8 @@
|
|||||||
:penpot:stroke-opacity (d/name (:stroke-opacity stroke))
|
:penpot:stroke-opacity (d/name (:stroke-opacity stroke))
|
||||||
:penpot:stroke-style (d/name (:stroke-style stroke))
|
:penpot:stroke-style (d/name (:stroke-style stroke))
|
||||||
:penpot:stroke-width (d/name (:stroke-width stroke))
|
:penpot:stroke-width (d/name (:stroke-width stroke))
|
||||||
|
:penpot:stroke-dash ((d/nilf str) (:stroke-dash stroke))
|
||||||
|
:penpot:stroke-gap ((d/nilf str) (:stroke-gap stroke))
|
||||||
:penpot:stroke-alignment (d/name (:stroke-alignment stroke))
|
:penpot:stroke-alignment (d/name (:stroke-alignment stroke))
|
||||||
:penpot:stroke-cap-start (d/name (:stroke-cap-start stroke))
|
:penpot:stroke-cap-start (d/name (:stroke-cap-start stroke))
|
||||||
:penpot:stroke-cap-end (d/name (:stroke-cap-end stroke))}]))]))))
|
:penpot:stroke-cap-end (d/name (:stroke-cap-end stroke))}]))]))))
|
||||||
|
|||||||
@ -30,6 +30,8 @@
|
|||||||
:stroke-style
|
:stroke-style
|
||||||
:stroke-alignment
|
:stroke-alignment
|
||||||
:stroke-width
|
:stroke-width
|
||||||
|
:stroke-dash
|
||||||
|
:stroke-gap
|
||||||
:stroke-color
|
:stroke-color
|
||||||
:stroke-color-ref-id
|
:stroke-color-ref-id
|
||||||
:stroke-color-ref-file
|
:stroke-color-ref-file
|
||||||
@ -113,6 +115,18 @@
|
|||||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
(st/emit! (dc/change-stroke-attrs ids {:stroke-width value} index))))
|
(st/emit! (dc/change-stroke-attrs ids {:stroke-width value} index))))
|
||||||
|
|
||||||
|
on-stroke-dash-change
|
||||||
|
(fn [index value]
|
||||||
|
(when-not (str/empty? value)
|
||||||
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
|
(st/emit! (dc/change-stroke-attrs ids {:stroke-dash value} index))))
|
||||||
|
|
||||||
|
on-stroke-gap-change
|
||||||
|
(fn [index value]
|
||||||
|
(when-not (str/empty? value)
|
||||||
|
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||||
|
(st/emit! (dc/change-stroke-attrs ids {:stroke-gap value} index))))
|
||||||
|
|
||||||
open-caps-select
|
open-caps-select
|
||||||
(fn [caps-state]
|
(fn [caps-state]
|
||||||
(fn [event]
|
(fn [event]
|
||||||
@ -226,6 +240,8 @@
|
|||||||
:on-color-change on-color-change
|
:on-color-change on-color-change
|
||||||
:on-color-detach on-color-detach
|
:on-color-detach on-color-detach
|
||||||
:on-stroke-width-change on-stroke-width-change
|
:on-stroke-width-change on-stroke-width-change
|
||||||
|
:on-stroke-dash-change on-stroke-dash-change
|
||||||
|
:on-stroke-gap-change on-stroke-gap-change
|
||||||
:on-stroke-style-change on-stroke-style-change
|
:on-stroke-style-change on-stroke-style-change
|
||||||
:on-stroke-alignment-change on-stroke-alignment-change
|
:on-stroke-alignment-change on-stroke-alignment-change
|
||||||
:open-caps-select open-caps-select
|
:open-caps-select open-caps-select
|
||||||
|
|||||||
@ -35,6 +35,8 @@
|
|||||||
on-color-detach
|
on-color-detach
|
||||||
on-remove
|
on-remove
|
||||||
on-stroke-width-change
|
on-stroke-width-change
|
||||||
|
on-stroke-dash-change
|
||||||
|
on-stroke-gap-change
|
||||||
on-stroke-style-change
|
on-stroke-style-change
|
||||||
on-stroke-alignment-change
|
on-stroke-alignment-change
|
||||||
on-stroke-cap-start-change
|
on-stroke-cap-start-change
|
||||||
@ -106,6 +108,23 @@
|
|||||||
ids
|
ids
|
||||||
#{:stroke-width})))
|
#{:stroke-width})))
|
||||||
|
|
||||||
|
;; The SVG renderer defaults dash and gap to `stroke-width + 10` when
|
||||||
|
;; unset. Showing that value as placeholder makes the override obvious.
|
||||||
|
default-dash-gap (when (number? stroke-width) (+ 10 stroke-width))
|
||||||
|
|
||||||
|
stroke-gap (or (:stroke-gap stroke) default-dash-gap)
|
||||||
|
stroke-dash (or (:stroke-dash stroke) default-dash-gap)
|
||||||
|
|
||||||
|
on-dash-change
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps index on-stroke-dash-change)
|
||||||
|
#(on-stroke-dash-change index %))
|
||||||
|
|
||||||
|
on-gap-change
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps index on-stroke-gap-change)
|
||||||
|
#(on-stroke-gap-change index %))
|
||||||
|
|
||||||
stroke-alignment (or (:stroke-alignment stroke) :center)
|
stroke-alignment (or (:stroke-alignment stroke) :center)
|
||||||
|
|
||||||
stroke-alignment-options
|
stroke-alignment-options
|
||||||
@ -294,6 +313,30 @@
|
|||||||
:disabled hidden?
|
:disabled hidden?
|
||||||
:on-change on-style-change}]])])
|
:on-change on-style-change}]])])
|
||||||
|
|
||||||
|
;; Stroke Dash / Gap (only visible for dashed style)
|
||||||
|
(when (= stroke-style :dashed)
|
||||||
|
[:div {:class (stl/css :stroke-dash-options)
|
||||||
|
:data-testid "stroke.dash-options"}
|
||||||
|
[:> numeric-input-wrapper* {:on-change on-dash-change
|
||||||
|
:text-icon "DASH"
|
||||||
|
:min 0
|
||||||
|
:on-focus on-focus
|
||||||
|
:on-blur on-blur
|
||||||
|
:attr :stroke-dash
|
||||||
|
:class (stl/css :numeric-input-wrapper)
|
||||||
|
:property (tr "workspace.options.stroke-dash")
|
||||||
|
:value stroke-dash}]
|
||||||
|
[:> numeric-input-wrapper* {:on-change on-gap-change
|
||||||
|
:text-icon "GAP"
|
||||||
|
:min 0
|
||||||
|
:on-focus on-focus
|
||||||
|
:on-blur on-blur
|
||||||
|
:attr :stroke-gap
|
||||||
|
:tooltip-placement "top-left"
|
||||||
|
:class (stl/css :numeric-input-wrapper)
|
||||||
|
:property (tr "workspace.options.stroke-gap")
|
||||||
|
:value stroke-gap}]])
|
||||||
|
|
||||||
;; Stroke Caps
|
;; Stroke Caps
|
||||||
(when show-caps
|
(when show-caps
|
||||||
[:div {:class (stl/css :stroke-caps-options)}
|
[:div {:class (stl/css :stroke-caps-options)}
|
||||||
|
|||||||
@ -77,6 +77,12 @@
|
|||||||
column-gap: var(--sp-xs);
|
column-gap: var(--sp-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stroke-dash-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
column-gap: var(--sp-xs);
|
||||||
|
}
|
||||||
|
|
||||||
.stroke-options-tokens {
|
.stroke-options-tokens {
|
||||||
@include sidebar.option-grid-structure;
|
@include sidebar.option-grid-structure;
|
||||||
|
|
||||||
|
|||||||
@ -724,13 +724,18 @@
|
|||||||
style (-> stroke :stroke-style sr/translate-stroke-style)
|
style (-> stroke :stroke-style sr/translate-stroke-style)
|
||||||
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
||||||
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
||||||
|
;; Sentinel -1 means "unset" on the Rust side — keeps the
|
||||||
|
;; FFI signature flat while letting the renderer fall back
|
||||||
|
;; to its default dash pattern when no override is stored.
|
||||||
|
dash (or (:stroke-dash stroke) -1)
|
||||||
|
gap (or (:stroke-gap stroke) -1)
|
||||||
offset (mem/alloc types.fills.impl/FILL-U8-SIZE)
|
offset (mem/alloc types.fills.impl/FILL-U8-SIZE)
|
||||||
heap (mem/get-heap-u8)
|
heap (mem/get-heap-u8)
|
||||||
dview (js/DataView. (.-buffer heap))]
|
dview (js/DataView. (.-buffer heap))]
|
||||||
(case align
|
(case align
|
||||||
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end)
|
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end dash gap)
|
||||||
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end)
|
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end dash gap)
|
||||||
(h/call wasm/internal-module "_add_shape_center_stroke" width style cap-start cap-end))
|
(h/call wasm/internal-module "_add_shape_center_stroke" width style cap-start cap-end dash gap))
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(some? gradient)
|
(some? gradient)
|
||||||
|
|||||||
@ -7560,6 +7560,14 @@ msgstr "Center"
|
|||||||
msgid "workspace.options.stroke.dashed"
|
msgid "workspace.options.stroke.dashed"
|
||||||
msgstr "Dashed"
|
msgstr "Dashed"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
|
||||||
|
msgid "workspace.options.stroke-dash"
|
||||||
|
msgstr "Dash"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
|
||||||
|
msgid "workspace.options.stroke-gap"
|
||||||
|
msgstr "Gap"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:138
|
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:138
|
||||||
msgid "workspace.options.stroke.dotted"
|
msgid "workspace.options.stroke.dotted"
|
||||||
msgstr "Dotted"
|
msgstr "Dotted"
|
||||||
|
|||||||
@ -41,6 +41,10 @@ pub struct Stroke {
|
|||||||
pub cap_end: Option<StrokeCap>,
|
pub cap_end: Option<StrokeCap>,
|
||||||
pub cap_start: Option<StrokeCap>,
|
pub cap_start: Option<StrokeCap>,
|
||||||
pub kind: StrokeKind,
|
pub kind: StrokeKind,
|
||||||
|
// Dash and gap overrides for the `Dashed` style. `None` falls back to the
|
||||||
|
// default `width + 10` pattern to keep existing designs visually identical.
|
||||||
|
pub dash: Option<f32>,
|
||||||
|
pub gap: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stroke {
|
impl Stroke {
|
||||||
@ -72,6 +76,8 @@ impl Stroke {
|
|||||||
style: StrokeStyle,
|
style: StrokeStyle,
|
||||||
cap_start: Option<StrokeCap>,
|
cap_start: Option<StrokeCap>,
|
||||||
cap_end: Option<StrokeCap>,
|
cap_end: Option<StrokeCap>,
|
||||||
|
dash: Option<f32>,
|
||||||
|
gap: Option<f32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Stroke {
|
Stroke {
|
||||||
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
||||||
@ -80,6 +86,8 @@ impl Stroke {
|
|||||||
cap_end,
|
cap_end,
|
||||||
cap_start,
|
cap_start,
|
||||||
kind: StrokeKind::Center,
|
kind: StrokeKind::Center,
|
||||||
|
dash,
|
||||||
|
gap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +96,8 @@ impl Stroke {
|
|||||||
style: StrokeStyle,
|
style: StrokeStyle,
|
||||||
cap_start: Option<StrokeCap>,
|
cap_start: Option<StrokeCap>,
|
||||||
cap_end: Option<StrokeCap>,
|
cap_end: Option<StrokeCap>,
|
||||||
|
dash: Option<f32>,
|
||||||
|
gap: Option<f32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Stroke {
|
Stroke {
|
||||||
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
||||||
@ -96,6 +106,8 @@ impl Stroke {
|
|||||||
cap_end,
|
cap_end,
|
||||||
cap_start,
|
cap_start,
|
||||||
kind: StrokeKind::Inner,
|
kind: StrokeKind::Inner,
|
||||||
|
dash,
|
||||||
|
gap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +116,8 @@ impl Stroke {
|
|||||||
style: StrokeStyle,
|
style: StrokeStyle,
|
||||||
cap_start: Option<StrokeCap>,
|
cap_start: Option<StrokeCap>,
|
||||||
cap_end: Option<StrokeCap>,
|
cap_end: Option<StrokeCap>,
|
||||||
|
dash: Option<f32>,
|
||||||
|
gap: Option<f32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Stroke {
|
Stroke {
|
||||||
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)),
|
||||||
@ -112,11 +126,19 @@ impl Stroke {
|
|||||||
cap_end,
|
cap_end,
|
||||||
cap_start,
|
cap_start,
|
||||||
kind: StrokeKind::Outer,
|
kind: StrokeKind::Outer,
|
||||||
|
dash,
|
||||||
|
gap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_content(&mut self, value: f32) {
|
pub fn scale_content(&mut self, value: f32) {
|
||||||
self.width *= value;
|
self.width *= value;
|
||||||
|
if let Some(dash) = self.dash {
|
||||||
|
self.dash = Some(dash * value);
|
||||||
|
}
|
||||||
|
if let Some(gap) = self.gap {
|
||||||
|
self.gap = Some(gap * value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the clip operation for dotted inner/outer strokes.
|
/// Returns the clip operation for dotted inner/outer strokes.
|
||||||
@ -256,7 +278,9 @@ impl Stroke {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
StrokeStyle::Dashed => {
|
StrokeStyle::Dashed => {
|
||||||
skia::PathEffect::dash(&[self.width + 10., self.width + 10.], 0.)
|
let dash = self.dash.unwrap_or(self.width + 10.);
|
||||||
|
let gap = self.gap.unwrap_or(self.width + 10.);
|
||||||
|
skia::PathEffect::dash(&[dash, gap], 0.)
|
||||||
}
|
}
|
||||||
StrokeStyle::Mixed => skia::PathEffect::dash(
|
StrokeStyle::Mixed => skia::PathEffect::dash(
|
||||||
&[
|
&[
|
||||||
|
|||||||
@ -68,8 +68,26 @@ impl TryFrom<RawStrokeCap> for StrokeCap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A negative value means "unset" — the renderer falls back to its default
|
||||||
|
// dash pattern. We use a sentinel instead of passing a bool because adding
|
||||||
|
// two f32 params keeps the FFI signature flat and allocation-free.
|
||||||
|
fn decode_optional(value: f32) -> Option<f32> {
|
||||||
|
if value.is_finite() && value >= 0.0 {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn add_shape_center_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) {
|
pub extern "C" fn add_shape_center_stroke(
|
||||||
|
width: f32,
|
||||||
|
style: u8,
|
||||||
|
cap_start: u8,
|
||||||
|
cap_end: u8,
|
||||||
|
dash: f32,
|
||||||
|
gap: f32,
|
||||||
|
) {
|
||||||
let stroke_style = RawStrokeStyle::from(style);
|
let stroke_style = RawStrokeStyle::from(style);
|
||||||
let cap_start = RawStrokeCap::from(cap_start);
|
let cap_start = RawStrokeCap::from(cap_start);
|
||||||
let cap_end = RawStrokeCap::from(cap_end);
|
let cap_end = RawStrokeCap::from(cap_end);
|
||||||
@ -80,12 +98,21 @@ pub extern "C" fn add_shape_center_stroke(width: f32, style: u8, cap_start: u8,
|
|||||||
stroke_style.into(),
|
stroke_style.into(),
|
||||||
cap_start.try_into().ok(),
|
cap_start.try_into().ok(),
|
||||||
cap_end.try_into().ok(),
|
cap_end.try_into().ok(),
|
||||||
|
decode_optional(dash),
|
||||||
|
decode_optional(gap),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn add_shape_inner_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) {
|
pub extern "C" fn add_shape_inner_stroke(
|
||||||
|
width: f32,
|
||||||
|
style: u8,
|
||||||
|
cap_start: u8,
|
||||||
|
cap_end: u8,
|
||||||
|
dash: f32,
|
||||||
|
gap: f32,
|
||||||
|
) {
|
||||||
let stroke_style = RawStrokeStyle::from(style);
|
let stroke_style = RawStrokeStyle::from(style);
|
||||||
let cap_start = RawStrokeCap::from(cap_start);
|
let cap_start = RawStrokeCap::from(cap_start);
|
||||||
let cap_end = RawStrokeCap::from(cap_end);
|
let cap_end = RawStrokeCap::from(cap_end);
|
||||||
@ -96,12 +123,21 @@ pub extern "C" fn add_shape_inner_stroke(width: f32, style: u8, cap_start: u8, c
|
|||||||
stroke_style.into(),
|
stroke_style.into(),
|
||||||
cap_start.try_into().ok(),
|
cap_start.try_into().ok(),
|
||||||
cap_end.try_into().ok(),
|
cap_end.try_into().ok(),
|
||||||
|
decode_optional(dash),
|
||||||
|
decode_optional(gap),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn add_shape_outer_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) {
|
pub extern "C" fn add_shape_outer_stroke(
|
||||||
|
width: f32,
|
||||||
|
style: u8,
|
||||||
|
cap_start: u8,
|
||||||
|
cap_end: u8,
|
||||||
|
dash: f32,
|
||||||
|
gap: f32,
|
||||||
|
) {
|
||||||
let stroke_style = RawStrokeStyle::from(style);
|
let stroke_style = RawStrokeStyle::from(style);
|
||||||
let cap_start = RawStrokeCap::from(cap_start);
|
let cap_start = RawStrokeCap::from(cap_start);
|
||||||
let cap_end = RawStrokeCap::from(cap_end);
|
let cap_end = RawStrokeCap::from(cap_end);
|
||||||
@ -112,6 +148,8 @@ pub extern "C" fn add_shape_outer_stroke(width: f32, style: u8, cap_start: u8, c
|
|||||||
stroke_style.into(),
|
stroke_style.into(),
|
||||||
cap_start.try_into().ok(),
|
cap_start.try_into().ok(),
|
||||||
cap_end.try_into().ok(),
|
cap_end.try_into().ok(),
|
||||||
|
decode_optional(dash),
|
||||||
|
decode_optional(gap),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user