0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-11 06:21:30 -05:00

🎉 Copy & paste images onto the workspace

This commit is contained in:
Andrés Moya 2020-06-02 10:48:51 +02:00
parent 94959ffff8
commit 7bf06353ad
6 changed files with 137 additions and 102 deletions

View file

@ -327,6 +327,65 @@
:fill-opacity 1
:shapes []})
(def ^:private default-color "#b1b2b5") ;; $color-gray-20
(def ^:private minimal-shapes
[{:type :rect
:name "Rect"
:fill-color default-color
:stroke-alignment :center}
{:type :image}
{:type :icon}
{:type :circle
:name "Circle"
:fill-color default-color}
{:type :path
:name "Path"
:stroke-style :solid
:stroke-color "#000000"
:stroke-width 2
:stroke-alignment :center
:fill-color "#000000"
:fill-opacity 0
:segments []}
{:type :frame
:stroke-style :none
:stroke-alignment :center
:name "Artboard"}
{:type :curve
:name "Path"
:stroke-style :solid
:stroke-color "#000000"
:stroke-width 2
:stroke-alignment :center
:fill-color "#000000"
:fill-opacity 0
:segments []}
{:type :text
:name "Text"
:content nil}])
(defn make-minimal-shape
[type]
(let [shape (d/seek #(= type (:type %)) minimal-shapes)]
(assert shape "unexpected shape type")
(assoc shape
:id (uuid/next)
:x 0
:y 0
:width 1
:height 1
:selrect {:x 0
:x1 0
:x2 0
:y 0
:y1 0
:y2 0
:width 1
:height 1}
:points []
:segments [])))
;; --- Changes Processing Impl
(defmulti process-change

View file

@ -508,6 +508,40 @@
(when (= :text (:type attrs))
(start-edition-mode id)))))))
(defn- calculate-centered-box
[state aspect-ratio]
(if (>= aspect-ratio 1)
(let [vbox (get-in state [:workspace-local :vbox])
width (/ (:width vbox) 2)
height (/ width aspect-ratio)
x (+ (:x vbox) (/ width 2))
y (+ (:y vbox) (/ (- (:height vbox) height) 2))]
[width height x y])
(let [vbox (get-in state [:workspace-local :vbox])
height (/ (:height vbox) 2)
width (* height aspect-ratio)
y (+ (:y vbox) (/ height 2))
x (+ (:x vbox) (/ (- (:width vbox) width) 2))]
[width height x y])))
(defn create-and-add-shape
[type data aspect-ratio]
(ptk/reify ::create-and-add-shape
ptk/WatchEvent
(watch [_ state stream]
(let [[width height x y] (calculate-centered-box state aspect-ratio)
shape (-> (cp/make-minimal-shape type)
(merge data)
(geom/resize width height)
(geom/absolute-move (gpt/point x y)))]
(rx/of (add-shape shape))))))
@ -1096,15 +1130,38 @@
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dws/select-shapes selected))))))
(defn- image-uploaded
[{:keys [id name] :as image}]
(let [shape {:name name
:metadata {:width (:width image)
:height (:height image)
:uri (:uri image)
:thumb-width (:thumb-width image)
:thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (create-and-add-shape :image shape aspect-ratio))))
(defn- paste-image-impl
[image]
(ptk/reify ::paste-bin-impl
ptk/WatchEvent
(watch [_ state stream]
(rx/of (dwp/upload-image image image-uploaded)))))
(def paste
(ptk/reify ::paste
ptk/WatchEvent
(watch [_ state stream]
(->> (rx/from (wapi/read-from-clipboard))
(->> (wapi/read-from-clipboard)
(rx/map t/decode)
(rx/filter #(= :copied-shapes (:type %)))
(rx/map #(select-keys % [:selected :objects]))
(rx/map paste-impl)
(rx/catch (partial instance? js/SyntaxError)
(fn [_]
(->> (wapi/read-image-from-clipboard)
(rx/map paste-image-impl))))
(rx/catch (fn [err]
(js/console.error "Clipboard error:" err)
(rx/empty)))))))

View file

@ -22,6 +22,7 @@
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.pages :as cp]
[uxbox.util.geom.path :as path]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.main.snap :as snap]
@ -36,100 +37,6 @@
(declare handle-finish-drawing)
(declare conditional-align)
(def ^:private default-color "#b1b2b5") ;; $color-gray-20
(def ^:private minimal-shapes
[{:type :rect
:name "Rect"
:fill-color default-color
:stroke-alignment :center}
{:type :image}
{:type :icon}
{:type :circle
:name "Circle"
:fill-color default-color}
{:type :path
:name "Path"
:stroke-style :solid
:stroke-color "#000000"
:stroke-width 2
:stroke-alignment :center
:fill-color "#000000"
:fill-opacity 0
:segments []}
{:type :frame
:stroke-style :none
:stroke-alignment :center
:name "Artboard"}
{:type :curve
:name "Path"
:stroke-style :solid
:stroke-color "#000000"
:stroke-width 2
:stroke-alignment :center
:fill-color "#000000"
:fill-opacity 0
:segments []}
{:type :text
:name "Text"
:content nil}])
(defn- make-minimal-shape
[type]
(let [tool (seek #(= type (:type %)) minimal-shapes)]
(assert tool "unexpected drawing tool")
(assoc tool
:id (uuid/next)
:x 0
:y 0
:width 1
:height 1
:selrect {:x 0
:x1 0
:x2 0
:y 0
:y1 0
:y2 0
:width 1
:height 1}
:points []
:segments [])))
(defn- calculate-centered-box
[state aspect-ratio]
(if (>= aspect-ratio 1)
(let [vbox (get-in state [:workspace-local :vbox])
width (/ (:width vbox) 2)
height (/ width aspect-ratio)
x (+ (:x vbox) (/ width 2))
y (+ (:y vbox) (/ (- (:height vbox) height) 2))]
[width height x y])
(let [vbox (get-in state [:workspace-local :vbox])
height (/ (:height vbox) 2)
width (* height aspect-ratio)
y (+ (:y vbox) (/ height 2))
x (+ (:x vbox) (/ (- (:width vbox) width) 2))]
[width height x y])))
(defn direct-add-shape
[type data aspect-ratio]
(ptk/reify ::direct-add-shape
ptk/WatchEvent
(watch [_ state stream]
(let [[width height x y] (calculate-centered-box state aspect-ratio)
shape (-> (make-minimal-shape type)
(merge data)
(geom/resize width height)
(geom/absolute-move (gpt/point x y)))]
(rx/of (dw/add-shape shape))))))
(defn start-drawing
[type]
{:pre [(keyword? type)]}
@ -155,7 +62,7 @@
(ptk/reify ::handle-drawing
ptk/UpdateEvent
(update [_ state]
(let [data (make-minimal-shape type)]
(let [data (cp/make-minimal-shape type)]
(update-in state [:workspace-local :drawing] merge data)))
ptk/WatchEvent

View file

@ -15,7 +15,6 @@
[uxbox.main.data.workspace :as dw]
[uxbox.main.store :as st]
[uxbox.main.ui.components.file-uploader :refer [file-uploader]]
[uxbox.main.ui.workspace.drawarea :refer [direct-add-shape]]
[uxbox.util.dom :as dom]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.main.ui.icons :as i]))
@ -42,7 +41,7 @@
:thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (direct-add-shape :image shape aspect-ratio))))
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
on-file-selected
(fn [file]

View file

@ -26,7 +26,7 @@
[uxbox.main.ui.hooks :as hooks]
[uxbox.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]]
[uxbox.main.ui.workspace.shapes.interactions :refer [interactions]]
[uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing direct-add-shape]]
[uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing]]
[uxbox.main.ui.workspace.selection :refer [selection-handlers]]
[uxbox.main.ui.workspace.presence :as presence]
[uxbox.main.ui.workspace.snap-points :refer [snap-points]]
@ -328,7 +328,7 @@
:thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (direct-add-shape :image shape aspect-ratio))))
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
on-drop
(fn [event]

View file

@ -13,6 +13,7 @@
[promesa.core :as p]
[beicon.core :as rx]
[cuerdas.core :as str]
[uxbox.common.data :as d]
[uxbox.util.transit :as t]))
(defn read-file-as-text
@ -79,8 +80,20 @@
(defn- read-from-clipboard
[]
(let [cboard (unchecked-get js/navigator "clipboard")]
(-> (.readText cboard)
(p/then identity))))
(rx/from (.readText cboard))))
(defn- read-image-from-clipboard
[]
(let [cboard (unchecked-get js/navigator "clipboard")
read-item (fn [item]
(let [img-type (->> (.-types item)
(d/seek #(str/starts-with? % "image/")))]
(if img-type
(rx/from (.getType item img-type))
(rx/empty))))]
(->> (rx/from (.read cboard)) ;; Get a stream of item lists
(rx/mapcat identity) ;; Convert each item into an emission
(rx/switch-map read-item))))
(defn request-fullscreen
[el]