mirror of
https://github.com/penpot/penpot.git
synced 2026-11-01 23:50:38 +00:00
* 🐛 Show the size badge on paths and straight lines The badge was hidden when the shape was smaller than the badge in either side. Flat paths have a zero width or height, so they never showed it, and straight two-point paths hid it on purpose. Paths now only compare their longest side with the badge width. A straight line compares its length, and the badge runs along the line, rotated with it and kept upright. Near the bottom of the viewport the badge moves to the other side of the line. The badge now reads the size from the shape :selrect, like the measures inputs, so a rotated path shows the same values in both places. The three badge branches share a single render block. AI-assisted-by: claude-opus-5-5 * ♻️ Extract the size badge layout and add tests for it Move the badge geometry out of selection-size-badge* into the pure size-badge-layout function, so it can be tested without rendering. The component now only picks the color and draws the result. Add unit tests for rects, flat paths and straight lines: when the badge hides, where it sits, how it rotates and how it moves when it reaches the bottom of the viewport. Keep the line rotation between -90 and 90 degrees instead of returning 360 for lines drawn leftward. The e2e test still expected straight lines to hide the badge. It now checks that the badge shows and follows the line angle. AI-assisted-by: claude-opus-5-5 * ✨ Show the length of straight lines in the size badge A straight two-point path is measured by its length, not by the box around it, so its size badge now shows the length with two decimals at most instead of width x height. Other shapes and paths keep width x height. The shorter text makes the badge narrower, so it also shows on shorter lines; the rule is the same, the badge needs the line to be at least as long as the badge. Relates to #12064 AI-assisted-by: claude-opus-5-5 * 🐛 Hide the path size badge when the path is lower than it Paths other than a straight line only hid the size badge when their longest side was narrower than it, so a wide but low path, or a narrow but tall one, still showed it. Flat paths no longer need that special case, since straight lines get their own length badge. Use the same rule as any other shape: hide the badge when either the width or the height of the path is smaller than the badge. AI-assisted-by: claude-opus-5-5
144 lines
6.0 KiB
Clojure
144 lines
6.0 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
|
|
|
(ns frontend-tests.ui.measurements-test
|
|
(:require
|
|
[app.common.geom.rect :as grc]
|
|
[app.common.math :as mth]
|
|
[app.common.types.shape :as cts]
|
|
[app.main.ui.measurements :as msr]
|
|
[cljs.test :as t :include-macros true]))
|
|
|
|
;; At zoom 1 the badge is 16 high, has 6 of horizontal padding, 6.5 per
|
|
;; character, and sits 8 away from the shape: its center is 16 away.
|
|
;; A flat path selrect has a minimum side of 0.01, which the measures
|
|
;; inputs show too.
|
|
|
|
(def ^:private big-vbox {:x -1000 :y -1000 :width 5000 :height 5000})
|
|
|
|
(defn- rect
|
|
[x y width height]
|
|
(cts/setup-shape {:type :rect :x x :y y :width width :height height}))
|
|
|
|
(defn- polyline
|
|
"A path through `points`, each a [x y] pair."
|
|
[& points]
|
|
(let [[[x0 y0] & others] points]
|
|
(cts/setup-shape
|
|
{:type :path
|
|
:content (into [{:command :move-to :params {:x x0 :y y0}}]
|
|
(map (fn [[x y]] {:command :line-to :params {:x x :y y}}))
|
|
others)})))
|
|
|
|
(defn- close-layout?
|
|
[expected layout]
|
|
(and (some? layout)
|
|
(every? (fn [[k v]]
|
|
(let [actual (get layout k)]
|
|
(if (number? v)
|
|
(mth/close? v actual 0.001)
|
|
(= v actual))))
|
|
expected)))
|
|
|
|
;; --- Plain shapes
|
|
|
|
(t/deftest rect-badge-sits-centered-below-the-shape
|
|
(let [layout (msr/size-badge-layout [(rect 0 0 100 50)] 1 big-vbox)]
|
|
;; "100 x 50" has 8 characters: 8 * 6.5 + 2 * 6
|
|
(t/is (close-layout? {:text "100 x 50" :width 64 :height 16
|
|
:cx 50 :cy 66 :rot 0}
|
|
layout))))
|
|
|
|
(t/deftest rect-narrower-than-the-badge-hides-it
|
|
(t/is (nil? (msr/size-badge-layout [(rect 0 0 40 40)] 1 big-vbox))))
|
|
|
|
(t/deftest rect-lower-than-the-badge-hides-it
|
|
(t/is (nil? (msr/size-badge-layout [(rect 0 0 200 10)] 1 big-vbox))))
|
|
|
|
(t/deftest badge-size-scales-with-the-zoom
|
|
;; At zoom 2 the badge is 28.75 x 8, so a 40 x 40 rect fits it.
|
|
(let [layout (msr/size-badge-layout [(rect 0 0 40 40)] 2 big-vbox)]
|
|
(t/is (close-layout? {:text "40 x 40" :width 28.75 :height 8 :cy 48}
|
|
layout))))
|
|
|
|
(t/deftest rect-badge-moves-above-at-the-vbox-bottom
|
|
(let [vbox {:x 0 :y 0 :width 1000 :height 60}
|
|
layout (msr/size-badge-layout [(rect 0 0 100 50)] 1 vbox)]
|
|
(t/is (close-layout? {:cx 50 :cy -16 :rot 0} layout))))
|
|
|
|
(t/deftest multiple-shapes-show-the-bounding-box
|
|
(let [layout (msr/size-badge-layout [(rect 0 0 100 50) (rect 200 100 100 50)]
|
|
1 big-vbox)]
|
|
(t/is (close-layout? {:text "300 x 150" :cx 150 :cy 166} layout))))
|
|
|
|
;; --- Paths
|
|
|
|
(t/deftest path-badge-sits-centered-below-the-path
|
|
;; Three points, so not a straight line: it uses the box badge.
|
|
(let [layout (msr/size-badge-layout [(polyline [0 0] [100 100] [200 0])]
|
|
1 big-vbox)]
|
|
(t/is (close-layout? {:text "200 x 100" :cx 100 :cy 116 :rot 0}
|
|
layout))))
|
|
|
|
(t/deftest path-lower-than-the-badge-hides-it
|
|
(t/is (nil? (msr/size-badge-layout [(polyline [0 0] [100 10] [200 0])]
|
|
1 big-vbox)))
|
|
;; A flat path, too: its height is the 0.01 minimum.
|
|
(t/is (nil? (msr/size-badge-layout [(polyline [0 100] [100 100] [200 100])]
|
|
1 big-vbox))))
|
|
|
|
(t/deftest path-narrower-than-the-badge-hides-it
|
|
(t/is (nil? (msr/size-badge-layout [(polyline [0 0] [10 100] [20 0])]
|
|
1 big-vbox))))
|
|
|
|
(t/deftest path-badge-reads-the-selrect-like-the-inputs
|
|
;; The points enclose a larger box than the selrect, as they do on a
|
|
;; rotated path. The text must match the measures inputs.
|
|
(let [path (-> (polyline [0 0] [100 50] [200 0])
|
|
(assoc :points (grc/rect->points (grc/make-rect 0 0 300 300))))
|
|
layout (msr/size-badge-layout [path] 1 big-vbox)]
|
|
(t/is (= "200 x 50" (:text layout)))))
|
|
|
|
;; --- Straight two-point lines
|
|
|
|
(t/deftest horizontal-line-badge-runs-below-the-line
|
|
(let [layout (msr/size-badge-layout [(polyline [0 50] [200 50])] 1 big-vbox)]
|
|
(t/is (close-layout? {:text "200" :cx 100 :cy 66 :rot 0} layout))))
|
|
|
|
(t/deftest line-drawn-right-to-left-keeps-the-text-upright
|
|
(let [layout (msr/size-badge-layout [(polyline [200 50] [0 50])] 1 big-vbox)]
|
|
(t/is (close-layout? {:cx 100 :cy 66 :rot 0} layout))))
|
|
|
|
(t/deftest vertical-line-badge-runs-along-the-line
|
|
(let [down (msr/size-badge-layout [(polyline [0 0] [0 200])] 1 big-vbox)
|
|
up (msr/size-badge-layout [(polyline [0 200] [0 0])] 1 big-vbox)]
|
|
(t/is (close-layout? {:text "200" :cx -16 :cy 100 :rot 90} down))
|
|
(t/is (close-layout? {:cx -16 :cy 100 :rot 90} up))))
|
|
|
|
(t/deftest diagonal-line-badge-follows-the-line-angle
|
|
(let [layout (msr/size-badge-layout [(polyline [0 0] [100 100])] 1 big-vbox)
|
|
offset (/ 16 (mth/sqrt 2))]
|
|
(t/is (close-layout? {:text "141.42" :cx (- 50 offset) :cy (+ 50 offset) :rot 45}
|
|
layout))))
|
|
|
|
(t/deftest line-shorter-than-the-badge-hides-it
|
|
;; "20" needs a 25 wide badge.
|
|
(t/is (nil? (msr/size-badge-layout [(polyline [0 0] [20 0])] 1 big-vbox))))
|
|
|
|
(t/deftest line-badge-shows-the-length-with-two-decimals-at-most
|
|
(t/is (= "70.71" (:text (msr/size-badge-layout [(polyline [0 0] [50 50])] 1 big-vbox))))
|
|
(t/is (= "50" (:text (msr/size-badge-layout [(polyline [0 0] [30 40])] 1 big-vbox)))))
|
|
|
|
(t/deftest diagonal-line-uses-its-length-to-fit-the-badge
|
|
;; Both sides (33) are narrower than the badge for "46.67" (44.5), but
|
|
;; the line is about 46.67 long, so the badge fits along it.
|
|
(t/is (some? (msr/size-badge-layout [(polyline [0 0] [33 33])] 1 big-vbox))))
|
|
|
|
(t/deftest line-badge-moves-to-the-other-side-at-the-vbox-bottom
|
|
(let [vbox {:x 0 :y 0 :width 1000 :height 60}
|
|
layout (msr/size-badge-layout [(polyline [0 50] [200 50])] 1 vbox)]
|
|
(t/is (close-layout? {:cx 100 :cy 34 :rot 0} layout))))
|