diff --git a/CHANGES.md b/CHANGES.md index 069288163..9106e1b2e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,8 @@ - Preserve components if possible, when pasted into a different file [Taiga #1063](https://tree.taiga.io/project/penpot/issue/1063). - Add the ability to offload file data to a cheaper storage when file becomes inactive. - Import/Export Penpot files from dashboard. -- Double click won't make a shape a path until you change a node [Taiga #] +- Double click won't make a shape a path until you change a node [Taiga #1796](https://tree.taiga.io/project/penpot/us/1796) +- Incremental area selection [#779](https://github.com/penpot/penpot/discussions/779) ### :bug: Bugs fixed diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 24165ff5c..c98eba760 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -208,3 +208,4 @@ (d/export gin/overlaps?) (d/export gin/has-point?) (d/export gin/has-point-rect?) +(d/export gin/rect-contains-shape?) diff --git a/common/src/app/common/geom/shapes/intersect.cljc b/common/src/app/common/geom/shapes/intersect.cljc index e4dc2cc56..4b0593dc1 100644 --- a/common/src/app/common/geom/shapes/intersect.cljc +++ b/common/src/app/common/geom/shapes/intersect.cljc @@ -302,3 +302,9 @@ (let [lines (points->lines (:points shape))] ;; TODO: Will only work for simple shapes (is-point-inside-evenodd? point lines))) + +(defn rect-contains-shape? + [rect shape] + (->> shape + :points + (every? (partial has-point-rect? rect)))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index b8dc5f08a..37c26e0f1 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1843,7 +1843,7 @@ (d/export dwc/select-shapes) (d/export dws/shift-select-shapes) (d/export dws/duplicate-selected) -(d/export dws/handle-selection) +(d/export dws/handle-area-selection) (d/export dws/select-inside-group) (d/export dwd/select-for-drawing) (d/export dwc/clear-edition-mode) diff --git a/frontend/src/app/main/data/workspace/path.cljs b/frontend/src/app/main/data/workspace/path.cljs index 57f2af43b..ada2f22a8 100644 --- a/frontend/src/app/main/data/workspace/path.cljs +++ b/frontend/src/app/main/data/workspace/path.cljs @@ -28,7 +28,7 @@ (d/export edition/move-selected) ;; Selection -(d/export selection/handle-selection) +(d/export selection/handle-area-selection) (d/export selection/select-node) (d/export selection/path-handler-enter) (d/export selection/path-handler-leave) diff --git a/frontend/src/app/main/data/workspace/path/selection.cljs b/frontend/src/app/main/data/workspace/path/selection.cljs index 46fd28cb5..3fed3f365 100644 --- a/frontend/src/app/main/data/workspace/path/selection.cljs +++ b/frontend/src/app/main/data/workspace/path/selection.cljs @@ -101,12 +101,12 @@ (update [_ state] (update state :workspace-local dissoc :selrect)))) -(defn handle-selection +(defn handle-area-selection [shift?] (letfn [(valid-rect? [{width :width height :height}] (or (> width 10) (> height 10)))] - (ptk/reify ::handle-selection + (ptk/reify ::handle-area-selection ptk/WatchEvent (watch [_ _ stream] (let [stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event))) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index fe72eafe2..eaeb4b972 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -45,7 +45,7 @@ (update [_ state] (assoc-in state [:workspace-local :selrect] selrect)))) -(defn handle-selection +(defn handle-area-selection [preserve?] (letfn [(data->selrect [data] (let [start (:start data) @@ -59,10 +59,11 @@ :y start-y :width (mth/abs (- end-x start-x)) :height (mth/abs (- end-y start-y))}))] - (ptk/reify ::handle-selection + (ptk/reify ::handle-area-selection ptk/WatchEvent - (watch [_ _ stream] - (let [stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event))) + (watch [_ state stream] + (let [zoom (get-in state [:workspace-local :zoom] 1) + stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event))) stoper (->> stream (rx/filter stop?))] (rx/concat (when-not preserve? @@ -74,11 +75,16 @@ {:start pos :stop pos})) nil) (rx/map data->selrect) - (rx/filter #(or (> (:width %) 10) - (> (:height %) 10))) - (rx/map update-selrect) + (rx/filter #(or (> (:width %) (/ 10 zoom)) + (> (:height %) (/ 10 zoom)))) + + (rx/flat-map + (fn [selrect] + (rx/of (update-selrect selrect) + (select-shapes-by-current-selrect preserve?)))) + (rx/take-until stoper)) - (rx/of (select-shapes-by-current-selrect preserve?)))))))) + (rx/of (update-selrect nil)))))))) ;; --- Toggle shape's selection status (selected or deselected) @@ -214,11 +220,12 @@ selrect (get-in state [:workspace-local :selrect]) blocked? (fn [id] (get-in objects [id :blocked] false))] (rx/merge - (rx/of (update-selrect nil)) (when selrect (->> (uw/ask! {:cmd :selection/query :page-id page-id - :rect selrect}) + :rect selrect + :include-frames? true + :full-frame? true}) (rx/map #(cp/clean-loops objects %)) (rx/map #(into initial-set (filter (comp not blocked?)) %)) (rx/map select-shapes)))))))) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 8df7cbdd0..2fbfbac08 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -67,10 +67,10 @@ node-editing? ;; Handle path node area selection - (st/emit! (dwdp/handle-selection shift?)) + (st/emit! (dwdp/handle-area-selection shift?)) (or (not id) (and frame? (not selected?))) - (st/emit! (dw/handle-selection shift?)) + (st/emit! (dw/handle-area-selection shift?)) (not drawing-tool) (st/emit! (when (or shift? (not selected?)) diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index d0a03d631..d93fcfaf4 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -84,7 +84,7 @@ (create-index new-objects))) (defn- query-index - [{index :index z-index :z-index} rect frame-id include-frames? include-groups? reverse?] + [{index :index z-index :z-index} rect frame-id include-frames? full-frame? include-groups? reverse?] (let [result (-> (qdt/search index (clj->js rect)) (es6-iterator-seq)) @@ -97,7 +97,11 @@ (case (:type shape) :frame include-frames? :group include-groups? - true))) + true) + + (or (not full-frame?) + (not= :frame (:type shape)) + (gsh/rect-contains-shape? rect shape)))) overlaps? (fn [shape] @@ -151,10 +155,10 @@ nil) (defmethod impl/handler :selection/query - [{:keys [page-id rect frame-id include-frames? include-groups? reverse?] - :or {include-groups? true reverse? false} :as message}] + [{:keys [page-id rect frame-id include-frames? full-frame? include-groups? reverse?] + :or {include-groups? true reverse? false include-frames? false full-frame? false} :as message}] (when-let [index (get @state page-id)] - (query-index index rect frame-id include-frames? include-groups? reverse?))) + (query-index index rect frame-id include-frames? full-frame? include-groups? reverse?))) (defmethod impl/handler :selection/query-z-index [{:keys [page-id objects ids]}]