mirror of
https://github.com/penpot/penpot.git
synced 2026-09-12 23:19:28 +00:00
Merge branch 'staging-render' into develop
This commit is contained in:
commit
2acf15958b
@ -116,6 +116,7 @@ example. It's still usable as before, we just removed the example.
|
|||||||
- Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841)
|
- Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841)
|
||||||
- Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492)
|
- Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492)
|
||||||
- Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843)
|
- Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843)
|
||||||
|
- Fix unicode handling on email template abbreviation filter [Github #7966](https://github.com/penpot/penpot/pull/7966)
|
||||||
|
|
||||||
## 2.11.1
|
## 2.11.1
|
||||||
|
|
||||||
|
|||||||
@ -240,4 +240,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -307,7 +307,8 @@
|
|||||||
:content-type (:mtype input)})]
|
:content-type (:mtype input)})]
|
||||||
(:id sobject))
|
(:id sobject))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/err :hint "unable to import profile picture"
|
(l/wrn :hint "unable to import profile picture"
|
||||||
|
:uri uri
|
||||||
:cause cause)
|
:cause cause)
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
|
|||||||
@ -104,28 +104,29 @@
|
|||||||
(def ^:private schema:limit
|
(def ^:private schema:limit
|
||||||
[:and
|
[:and
|
||||||
[:map
|
[:map
|
||||||
[::name :any]
|
[::name :keyword]
|
||||||
[::strategy schema:strategy]
|
[::strategy schema:strategy]
|
||||||
[::key :string]
|
[::key :string]
|
||||||
[::opts :string]]
|
[::opts :string]
|
||||||
[:or
|
[::capacity {:optional true} ::sm/int]
|
||||||
[:map
|
[::rate {:optional true} ::sm/int]
|
||||||
[::capacity ::sm/int]
|
[::interval {:optional true} ::ct/duration]
|
||||||
[::rate ::sm/int]
|
[::params {:optional true} [::sm/vec :any]]
|
||||||
[::internal ::ct/duration]
|
[::permits {:optional true} ::sm/int]
|
||||||
[::params [::sm/vec :any]]]
|
[::unit {:optional true} [:enum :days :hours :minutes :seconds :weeks]]]
|
||||||
[:map
|
[:fn (fn [attrs]
|
||||||
[::nreq ::sm/int]
|
(let [contains-fn (partial contains? attrs)]
|
||||||
[::unit [:enum :days :hours :minutes :seconds :weeks]]]]])
|
(or (every? contains-fn [::capacity ::rate ::interval])
|
||||||
|
(every? contains-fn [::permits ::unit]))))]])
|
||||||
|
|
||||||
(def ^:private schema:limits
|
(def ^:private schema:limits
|
||||||
[:map-of :keyword [::sm/vec schema:limit]])
|
[:map-of :keyword [::sm/vec schema:limit]])
|
||||||
|
|
||||||
(def ^:private valid-limit-tuple?
|
(def ^:private valid-limit-tuple?
|
||||||
(sm/lazy-validator schema:limit-tuple))
|
(sm/validator schema:limit-tuple))
|
||||||
|
|
||||||
(def ^:private valid-rlimit-instance?
|
(def ^:private valid-rlimit-instance?
|
||||||
(sm/lazy-validator ::rpc/rlimit))
|
(sm/validator ::rpc/rlimit))
|
||||||
|
|
||||||
(defmethod parse-limit :window
|
(defmethod parse-limit :window
|
||||||
[[name strategy opts :as vlimit]]
|
[[name strategy opts :as vlimit]]
|
||||||
@ -134,16 +135,16 @@
|
|||||||
(merge
|
(merge
|
||||||
{::name name
|
{::name name
|
||||||
::strategy strategy}
|
::strategy strategy}
|
||||||
(if-let [[_ nreq unit] (re-find window-opts-re opts)]
|
(if-let [[_ permits unit] (re-find window-opts-re opts)]
|
||||||
(let [nreq (parse-long nreq)]
|
(let [permits (parse-long permits)]
|
||||||
{::nreq nreq
|
{::permits permits
|
||||||
::unit (case unit
|
::unit (case unit
|
||||||
"d" :days
|
"d" :days
|
||||||
"h" :hours
|
"h" :hours
|
||||||
"m" :minutes
|
"m" :minutes
|
||||||
"s" :seconds
|
"s" :seconds
|
||||||
"w" :weeks)
|
"w" :weeks)
|
||||||
::key (str "ratelimit.window." (d/name name))
|
::key (str "penpot.rlimit." (cf/get :tenant) ".window." (d/name name))
|
||||||
::opts opts})
|
::opts opts})
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :invalid-window-limit-opts
|
:code :invalid-window-limit-opts
|
||||||
@ -164,15 +165,15 @@
|
|||||||
::interval interval
|
::interval interval
|
||||||
::opts opts
|
::opts opts
|
||||||
::params [(->seconds interval) rate capacity]
|
::params [(->seconds interval) rate capacity]
|
||||||
::key (str "ratelimit.bucket." (d/name name))})
|
::key (str "penpot.rlimit." (cf/get :tenant) ".bucket." (d/name name))})
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :invalid-bucket-limit-opts
|
:code :invalid-bucket-limit-opts
|
||||||
:hint (str/ffmt "looks like '%' does not have a valid format" opts))))
|
:hint (str/ffmt "looks like '%' does not have a valid format" opts))))
|
||||||
|
|
||||||
(defmethod process-limit :bucket
|
(defmethod process-limit :bucket
|
||||||
[rconn user-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}]
|
[rconn profile-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}]
|
||||||
(let [script (-> bucket-rate-limit-script
|
(let [script (-> bucket-rate-limit-script
|
||||||
(assoc ::rscript/keys [(str key "." service "." user-id)])
|
(assoc ::rscript/keys [(str key "." service "." profile-id)])
|
||||||
(assoc ::rscript/vals (conj params (->seconds now))))
|
(assoc ::rscript/vals (conj params (->seconds now))))
|
||||||
result (rds/eval rconn script)
|
result (rds/eval rconn script)
|
||||||
allowed? (boolean (nth result 0))
|
allowed? (boolean (nth result 0))
|
||||||
@ -192,18 +193,18 @@
|
|||||||
(assoc ::lresult/remaining remaining))))
|
(assoc ::lresult/remaining remaining))))
|
||||||
|
|
||||||
(defmethod process-limit :window
|
(defmethod process-limit :window
|
||||||
[rconn user-id now {:keys [::nreq ::unit ::key ::service] :as limit}]
|
[rconn profile-id now {:keys [::permits ::unit ::key ::service] :as limit}]
|
||||||
(let [ts (ct/truncate now unit)
|
(let [ts (ct/truncate now unit)
|
||||||
ttl (ct/diff now (ct/plus ts {unit 1}))
|
ttl (ct/diff now (ct/plus ts {unit 1}))
|
||||||
script (-> window-rate-limit-script
|
script (-> window-rate-limit-script
|
||||||
(assoc ::rscript/keys [(str key "." service "." user-id "." (ct/format-inst ts))])
|
(assoc ::rscript/keys [(str key "." service "." profile-id "." (ct/format-inst ts))])
|
||||||
(assoc ::rscript/vals [nreq (->seconds ttl)]))
|
(assoc ::rscript/vals [permits (->seconds ttl)]))
|
||||||
result (rds/eval rconn script)
|
result (rds/eval rconn script)
|
||||||
allowed? (boolean (nth result 0))
|
allowed? (boolean (nth result 0))
|
||||||
remaining (nth result 1)]
|
remaining (nth result 1)]
|
||||||
(l/trace :hint "limit processed"
|
(l/trace :hint "limit processed"
|
||||||
:service service
|
:service service
|
||||||
:limit (name (::name limit))
|
:name (name (::name limit))
|
||||||
:strategy (name (::strategy limit))
|
:strategy (name (::strategy limit))
|
||||||
:opts (::opts limit)
|
:opts (::opts limit)
|
||||||
:allowed allowed?
|
:allowed allowed?
|
||||||
@ -214,8 +215,8 @@
|
|||||||
(assoc ::lresult/reset (ct/plus ts {unit 1})))))
|
(assoc ::lresult/reset (ct/plus ts {unit 1})))))
|
||||||
|
|
||||||
(defn- process-limits
|
(defn- process-limits
|
||||||
[rconn user-id limits now]
|
[rconn profile-id limits now]
|
||||||
(let [results (into [] (map (partial process-limit rconn user-id now)) limits)
|
(let [results (into [] (map (partial process-limit rconn profile-id now)) limits)
|
||||||
remaining (->> results
|
remaining (->> results
|
||||||
(d/index-by ::name ::lresult/remaining)
|
(d/index-by ::name ::lresult/remaining)
|
||||||
(uri/map->query-string))
|
(uri/map->query-string))
|
||||||
@ -227,7 +228,7 @@
|
|||||||
|
|
||||||
(when rejected
|
(when rejected
|
||||||
(l/warn :hint "rejected rate limit"
|
(l/warn :hint "rejected rate limit"
|
||||||
:user-id (str user-id)
|
:profile-id (str profile-id)
|
||||||
:limit-service (-> rejected ::service name)
|
:limit-service (-> rejected ::service name)
|
||||||
:limit-name (-> rejected ::name name)
|
:limit-name (-> rejected ::name name)
|
||||||
:limit-strategy (-> rejected ::strategy name)))
|
:limit-strategy (-> rejected ::strategy name)))
|
||||||
@ -371,12 +372,9 @@
|
|||||||
(defn- on-refresh-error
|
(defn- on-refresh-error
|
||||||
[_ cause]
|
[_ cause]
|
||||||
(when-not (instance? java.util.concurrent.RejectedExecutionException cause)
|
(when-not (instance? java.util.concurrent.RejectedExecutionException cause)
|
||||||
(if-let [explain (-> cause ex-data ex/explain)]
|
(l/warn :hint "unexpected exception on loading config"
|
||||||
(l/warn ::l/raw (str "unable to refresh config, invalid format:\n" explain)
|
:cause cause
|
||||||
::l/sync? true)
|
::l/sync? true)))
|
||||||
(l/warn :hint "unexpected exception on loading config"
|
|
||||||
:cause cause
|
|
||||||
::l/sync? true))))
|
|
||||||
|
|
||||||
(defn- get-config-path
|
(defn- get-config-path
|
||||||
[]
|
[]
|
||||||
|
|||||||
@ -25,9 +25,9 @@ local allowed = filled >= requested
|
|||||||
local newTokens = filled
|
local newTokens = filled
|
||||||
if allowed then
|
if allowed then
|
||||||
newTokens = filled - requested
|
newTokens = filled - requested
|
||||||
|
redis.call("hset", tokensKey, "tokens", newTokens, "timestamp", timestamp)
|
||||||
end
|
end
|
||||||
|
|
||||||
redis.call("hset", tokensKey, "tokens", newTokens, "timestamp", timestamp)
|
|
||||||
redis.call("expire", tokensKey, ttl)
|
redis.call("expire", tokensKey, ttl)
|
||||||
|
|
||||||
return { allowed, newTokens }
|
return { allowed, newTokens }
|
||||||
|
|||||||
@ -7,10 +7,18 @@
|
|||||||
(ns app.util.template
|
(ns app.util.template
|
||||||
(:require
|
(:require
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[selmer.filters :as sf]
|
||||||
[selmer.parser :as sp]))
|
[selmer.parser :as sp]))
|
||||||
|
|
||||||
;; (sp/cache-off!)
|
;; (sp/cache-off!)
|
||||||
|
|
||||||
|
(sf/add-filter! :abbreviate
|
||||||
|
(fn [s n]
|
||||||
|
(let [n (parse-long n)]
|
||||||
|
(str/abbreviate s n))))
|
||||||
|
|
||||||
|
|
||||||
(defn render
|
(defn render
|
||||||
[path context]
|
[path context]
|
||||||
(try
|
(try
|
||||||
|
|||||||
@ -137,33 +137,34 @@ RETURNING task.id, task.queue")
|
|||||||
::wait)))
|
::wait)))
|
||||||
|
|
||||||
(run-batch []
|
(run-batch []
|
||||||
(let [rconn (rds/connect cfg)]
|
(try
|
||||||
(try
|
(let [rconn (rds/connect cfg)]
|
||||||
(-> cfg
|
(try
|
||||||
(assoc ::rds/conn rconn)
|
(-> cfg
|
||||||
(db/tx-run! run-batch'))
|
(assoc ::rds/conn rconn)
|
||||||
|
(db/tx-run! run-batch'))
|
||||||
|
(finally
|
||||||
|
(.close ^AutoCloseable rconn))))
|
||||||
|
|
||||||
(catch InterruptedException cause
|
(catch InterruptedException cause
|
||||||
(throw cause))
|
(throw cause))
|
||||||
(catch Exception cause
|
|
||||||
(cond
|
|
||||||
(rds/exception? cause)
|
|
||||||
(do
|
|
||||||
(l/wrn :hint "redis exception (will retry in an instant)" :cause cause)
|
|
||||||
(px/sleep timeout))
|
|
||||||
|
|
||||||
(db/sql-exception? cause)
|
(catch Exception cause
|
||||||
(do
|
(cond
|
||||||
(l/wrn :hint "database exception (will retry in an instant)" :cause cause)
|
(rds/exception? cause)
|
||||||
(px/sleep timeout))
|
(do
|
||||||
|
(l/wrn :hint "redis exception (will retry in an instant)" :cause cause)
|
||||||
|
(px/sleep timeout))
|
||||||
|
|
||||||
:else
|
(db/sql-exception? cause)
|
||||||
(do
|
(do
|
||||||
(l/err :hint "unhandled exception (will retry in an instant)" :cause cause)
|
(l/wrn :hint "database exception (will retry in an instant)" :cause cause)
|
||||||
(px/sleep timeout))))
|
(px/sleep timeout))
|
||||||
|
|
||||||
(finally
|
:else
|
||||||
(.close ^AutoCloseable rconn)))))
|
(do
|
||||||
|
(l/err :hint "unhandled exception (will retry in an instant)" :cause cause)
|
||||||
|
(px/sleep timeout))))))
|
||||||
|
|
||||||
(dispatcher []
|
(dispatcher []
|
||||||
(l/inf :hint "started")
|
(l/inf :hint "started")
|
||||||
@ -176,7 +177,7 @@ RETURNING task.id, task.queue")
|
|||||||
(catch InterruptedException _
|
(catch InterruptedException _
|
||||||
(l/trc :hint "interrupted"))
|
(l/trc :hint "interrupted"))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/err :hint " unexpected exception" :cause cause))
|
(l/err :hint "unexpected exception" :cause cause))
|
||||||
(finally
|
(finally
|
||||||
(l/inf :hint "terminated"))))]
|
(l/inf :hint "terminated"))))]
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
integrant/integrant {:mvn/version "1.0.0"}
|
integrant/integrant {:mvn/version "1.0.0"}
|
||||||
|
|
||||||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||||
funcool/cuerdas {:mvn/version "2025.06.16-414"}
|
funcool/cuerdas {:mvn/version "2026.415"}
|
||||||
funcool/promesa
|
funcool/promesa
|
||||||
{:git/sha "46048fc0d4bf5466a2a4121f5d52aefa6337f2e8"
|
{:git/sha "46048fc0d4bf5466a2a4121f5d52aefa6337f2e8"
|
||||||
:git/url "https://github.com/funcool/promesa"}
|
:git/url "https://github.com/funcool/promesa"}
|
||||||
|
|||||||
@ -1410,8 +1410,8 @@ Will return a value that matches this schema:
|
|||||||
;; NOTE: we can't assign statically at eval time the value of a
|
;; NOTE: we can't assign statically at eval time the value of a
|
||||||
;; function that is declared but not defined; so we need to pass
|
;; function that is declared but not defined; so we need to pass
|
||||||
;; an anonymous function and delegate the resolution to runtime
|
;; an anonymous function and delegate the resolution to runtime
|
||||||
{:encode/json #(export-dtcg-json %)
|
{:encode/json #(some-> % export-dtcg-json)
|
||||||
:decode/json #(read-multi-set-dtcg %)
|
:decode/json #(some-> % read-multi-set-dtcg)
|
||||||
;; FIXME: add better, more reallistic generator
|
;; FIXME: add better, more reallistic generator
|
||||||
:gen/gen (->> (sg/small-int)
|
:gen/gen (->> (sg/small-int)
|
||||||
(sg/fmap (fn [_]
|
(sg/fmap (fn [_]
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
[app.common.types.fills :as types.fills]
|
[app.common.types.fills :as types.fills]
|
||||||
[app.common.types.library :as ctl]
|
[app.common.types.library :as ctl]
|
||||||
[app.common.types.shape :as shp]
|
[app.common.types.shape :as shp]
|
||||||
[app.common.types.shape.shadow :refer [check-shadow]]
|
[app.common.types.shape.shadow :as types.shadow]
|
||||||
[app.common.types.text :as txt]
|
[app.common.types.text :as txt]
|
||||||
[app.main.broadcast :as mbc]
|
[app.main.broadcast :as mbc]
|
||||||
[app.main.data.helpers :as dsh]
|
[app.main.data.helpers :as dsh]
|
||||||
@ -406,30 +406,30 @@
|
|||||||
|
|
||||||
(defn change-shadow
|
(defn change-shadow
|
||||||
[ids attrs index]
|
[ids attrs index]
|
||||||
(ptk/reify ::change-shadow
|
(letfn [(update-shadow [shape]
|
||||||
ptk/WatchEvent
|
(let [;; If we try to set a gradient to a shadow (for
|
||||||
(watch [_ _ _]
|
;; example using the color selection from
|
||||||
(rx/of (dwsh/update-shapes
|
;; multiple shapes) let's use the first stop
|
||||||
ids
|
;; color
|
||||||
(fn [shape]
|
attrs (cond-> attrs
|
||||||
(let [;; If we try to set a gradient to a shadow (for
|
(:gradient attrs)
|
||||||
;; example using the color selection from
|
(-> (dm/get-in [:gradient :stops 0])
|
||||||
;; multiple shapes) let's use the first stop
|
(select-keys types.shadow/color-attrs)))
|
||||||
;; color
|
|
||||||
attrs (cond-> attrs
|
|
||||||
(:gradient attrs)
|
|
||||||
(dm/get-in [:gradient :stops 0]))
|
|
||||||
|
|
||||||
attrs' (-> (dm/get-in shape [:shadow index :color])
|
attrs' (-> (dm/get-in shape [:shadow index :color])
|
||||||
(merge attrs)
|
(merge attrs)
|
||||||
(d/without-nils))]
|
(d/without-nils))]
|
||||||
(assoc-in shape [:shadow index :color] attrs'))))))))
|
(assoc-in shape [:shadow index :color] attrs')))]
|
||||||
|
(ptk/reify ::change-shadow
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ _ _]
|
||||||
|
(rx/of (dwsh/update-shapes ids update-shadow))))))
|
||||||
|
|
||||||
(defn add-shadow
|
(defn add-shadow
|
||||||
[ids shadow]
|
[ids shadow]
|
||||||
|
|
||||||
(assert
|
(assert
|
||||||
(check-shadow shadow)
|
(types.shadow/check-shadow shadow)
|
||||||
"expected a valid shadow struct")
|
"expected a valid shadow struct")
|
||||||
|
|
||||||
(assert
|
(assert
|
||||||
@ -1146,16 +1146,16 @@
|
|||||||
(defn- shadow->color-attr
|
(defn- shadow->color-attr
|
||||||
"Given a stroke map enriched with :shape-id, :index, and optionally
|
"Given a stroke map enriched with :shape-id, :index, and optionally
|
||||||
:has-token-applied / :token-name, returns a color attribute map.
|
:has-token-applied / :token-name, returns a color attribute map.
|
||||||
|
|
||||||
If :has-token-applied is true, adds token metadata to :attrs:
|
If :has-token-applied is true, adds token metadata to :attrs:
|
||||||
{:has-token-applied true
|
{:has-token-applied true
|
||||||
:token-name <token-name>}
|
:token-name <token-name>}
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
- stroke: map with stroke info, including :shape-id and :index
|
- stroke: map with stroke info, including :shape-id and :index
|
||||||
- file-id: current file UUID
|
- file-id: current file UUID
|
||||||
- libraries: map of shared color libraries
|
- libraries: map of shared color libraries
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A map like:
|
A map like:
|
||||||
{:attrs {...color data...}
|
{:attrs {...color data...}
|
||||||
@ -1260,12 +1260,12 @@
|
|||||||
will include extra attributes in its :attrs map:
|
will include extra attributes in its :attrs map:
|
||||||
{:has-token-applied true
|
{:has-token-applied true
|
||||||
:token-name <token-name>}
|
:token-name <token-name>}
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
- shapes: vector of shape maps
|
- shapes: vector of shape maps
|
||||||
- file-id: current file UUID
|
- file-id: current file UUID
|
||||||
- libraries: map of shared color libraries
|
- libraries: map of shared color libraries
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A vector of color attribute maps with metadata for each shape."
|
A vector of color attribute maps with metadata for each shape."
|
||||||
[shapes file-id libraries]
|
[shapes file-id libraries]
|
||||||
|
|||||||
@ -277,7 +277,6 @@ fn propagate_reflow(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let shapes = &state.shapes;
|
let shapes = &state.shapes;
|
||||||
let mut reflow_parent = false;
|
|
||||||
|
|
||||||
if reflown.contains(id) {
|
if reflown.contains(id) {
|
||||||
return;
|
return;
|
||||||
@ -294,15 +293,10 @@ fn propagate_reflow(
|
|||||||
// If this is a fill layout but the parent has not been reflown yet
|
// If this is a fill layout but the parent has not been reflown yet
|
||||||
// we wait for the next iteration for reflow
|
// we wait for the next iteration for reflow
|
||||||
skip_reflow = true;
|
skip_reflow = true;
|
||||||
reflow_parent = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if shape.is_layout_vertical_auto() || shape.is_layout_horizontal_auto() {
|
|
||||||
reflow_parent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skip_reflow {
|
if !skip_reflow {
|
||||||
layout_reflows.push(*id);
|
layout_reflows.push(*id);
|
||||||
}
|
}
|
||||||
@ -312,32 +306,26 @@ fn propagate_reflow(
|
|||||||
if let Some(child) = shapes.get(&children_ids[0]) {
|
if let Some(child) = shapes.get(&children_ids[0]) {
|
||||||
let child_bounds = bounds.find(child);
|
let child_bounds = bounds.find(child);
|
||||||
bounds.insert(shape.id, child_bounds);
|
bounds.insert(shape.id, child_bounds);
|
||||||
reflow_parent = true;
|
|
||||||
}
|
}
|
||||||
reflown.insert(*id);
|
reflown.insert(*id);
|
||||||
}
|
}
|
||||||
Type::Group(_) => {
|
Type::Group(_) => {
|
||||||
if let Some(shape_bounds) = calculate_group_bounds(shape, shapes, bounds) {
|
if let Some(shape_bounds) = calculate_group_bounds(shape, shapes, bounds) {
|
||||||
bounds.insert(shape.id, shape_bounds);
|
bounds.insert(shape.id, shape_bounds);
|
||||||
reflow_parent = true;
|
|
||||||
}
|
}
|
||||||
reflown.insert(*id);
|
reflown.insert(*id);
|
||||||
}
|
}
|
||||||
Type::Bool(_) => {
|
Type::Bool(_) => {
|
||||||
if let Some(shape_bounds) = calculate_bool_bounds(shape, shapes, bounds, modifiers) {
|
if let Some(shape_bounds) = calculate_bool_bounds(shape, shapes, bounds, modifiers) {
|
||||||
bounds.insert(shape.id, shape_bounds);
|
bounds.insert(shape.id, shape_bounds);
|
||||||
reflow_parent = true;
|
|
||||||
}
|
}
|
||||||
reflown.insert(*id);
|
reflown.insert(*id);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {}
|
||||||
// Other shapes don't have to be reflown
|
|
||||||
reflow_parent = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
|
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
|
||||||
if reflow_parent && (parent.has_layout() || parent.is_group_like()) {
|
if parent.has_layout() || parent.is_group_like() {
|
||||||
entries.push_back(Modifier::reflow(parent.id));
|
entries.push_back(Modifier::reflow(parent.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -344,6 +344,7 @@ fn distribute_fill_across_space(layout_axis: &LayoutAxis, tracks: &mut [TrackDat
|
|||||||
let mut size =
|
let mut size =
|
||||||
track.across_size - child.margin_across_start - child.margin_across_end;
|
track.across_size - child.margin_across_start - child.margin_across_end;
|
||||||
size = size.clamp(child.min_across_size, child.max_across_size);
|
size = size.clamp(child.min_across_size, child.max_across_size);
|
||||||
|
size = f32::min(size, layout_axis.across_space());
|
||||||
child.across_size = size;
|
child.across_size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -545,14 +546,22 @@ fn child_position(
|
|||||||
align_self: Some(align_self),
|
align_self: Some(align_self),
|
||||||
..
|
..
|
||||||
}) => match align_self {
|
}) => match align_self {
|
||||||
AlignSelf::Center => (track.across_size - child_axis.across_size) / 2.0,
|
AlignSelf::Center => {
|
||||||
|
(track.across_size - child_axis.across_size + child_axis.margin_across_start
|
||||||
|
- child_axis.margin_across_end)
|
||||||
|
/ 2.0
|
||||||
|
}
|
||||||
AlignSelf::End => {
|
AlignSelf::End => {
|
||||||
track.across_size - child_axis.across_size - child_axis.margin_across_end
|
track.across_size - child_axis.across_size - child_axis.margin_across_end
|
||||||
}
|
}
|
||||||
_ => child_axis.margin_across_start,
|
_ => child_axis.margin_across_start,
|
||||||
},
|
},
|
||||||
_ => match layout_data.align_items {
|
_ => match layout_data.align_items {
|
||||||
AlignItems::Center => (track.across_size - child_axis.across_size) / 2.0,
|
AlignItems::Center => {
|
||||||
|
(track.across_size - child_axis.across_size + child_axis.margin_across_start
|
||||||
|
- child_axis.margin_across_end)
|
||||||
|
/ 2.0
|
||||||
|
}
|
||||||
AlignItems::End => {
|
AlignItems::End => {
|
||||||
track.across_size - child_axis.across_size - child_axis.margin_across_end
|
track.across_size - child_axis.across_size - child_axis.margin_across_end
|
||||||
}
|
}
|
||||||
@ -578,7 +587,11 @@ pub fn reflow_flex_layout(
|
|||||||
let tracks = calculate_track_data(shape, layout_data, flex_data, layout_bounds, shapes, bounds);
|
let tracks = calculate_track_data(shape, layout_data, flex_data, layout_bounds, shapes, bounds);
|
||||||
|
|
||||||
for track in tracks.iter() {
|
for track in tracks.iter() {
|
||||||
let total_shapes_size = track.shapes.iter().map(|s| s.main_size).sum::<f32>();
|
let total_shapes_size = track
|
||||||
|
.shapes
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.main_size + s.margin_main_start + s.margin_main_end)
|
||||||
|
.sum::<f32>();
|
||||||
let mut shape_anchor = first_anchor(layout_data, &layout_axis, track, total_shapes_size);
|
let mut shape_anchor = first_anchor(layout_data, &layout_axis, track, total_shapes_size);
|
||||||
|
|
||||||
for child_axis in track.shapes.iter() {
|
for child_axis in track.shapes.iter() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user