From 94e22902f0264a5598284df6a26b9e5b1d64a5b9 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 27 Feb 2016 13:49:26 +0200 Subject: [PATCH] Initial (buggy) implementation of text drawing tool. --- src/uxbox/shapes.cljs | 34 +++- src/uxbox/ui/mixins.cljs | 6 + src/uxbox/ui/shapes.cljs | 1 + src/uxbox/ui/shapes/text.cljs | 147 ++++++++++++++++++ src/uxbox/ui/workspace.cljs | 4 +- src/uxbox/ui/workspace/canvas/draw.cljs | 4 +- src/uxbox/ui/workspace/sidebar/drawtools.cljs | 15 +- src/uxbox/ui/workspace/sidebar/layers.cljs | 1 + 8 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 src/uxbox/ui/shapes/text.cljs diff --git a/src/uxbox/shapes.cljs b/src/uxbox/shapes.cljs index 3ed46ed56..f1c03f999 100644 --- a/src/uxbox/shapes.cljs +++ b/src/uxbox/shapes.cljs @@ -15,7 +15,7 @@ (derive $ :builtin/rect ::rect) (derive $ :builtin/line ::shape) (derive $ :builtin/circle ::shape) - (derive $ :builtin/text ::shape) + (derive $ :builtin/text ::rect) (derive $ :builtin/group ::rect))) (defn shape? @@ -135,9 +135,15 @@ (assoc shape :x2 x :y2 x) (assoc shape :x2 x :y2 y))) +(defmethod resize :builtin/text + [shape {:keys [x y lock] :as pos}] + (if lock + (assoc shape :x2 x :y2 x) + (assoc shape :x2 x :y2 y))) + (defmethod resize :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (resize)" (select-keys shape [:type])))) (defmethod resize' ::rect [shape {:keys [width height] :as size}] @@ -149,7 +155,7 @@ (defmethod resize' :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (resize')" (select-keys shape [:type])))) (defmethod size ::rect [{:keys [x1 y1 x2 y2] :as shape}] @@ -158,7 +164,7 @@ (defmethod size :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (size)" (select-keys shape [:type])))) ;; Move @@ -192,7 +198,7 @@ (defmethod move :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (move)" (select-keys shape [:type])))) (defmethod move' ::rect [shape {:keys [x y] :as pos}] @@ -214,7 +220,7 @@ (defmethod move' :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (move')" (select-keys shape [:type])))) (defmethod rotate ::shape [shape rotation] @@ -273,7 +279,7 @@ (defmethod outer-rect' :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (outer-rect')" (select-keys shape [:type])))) (defmethod -transformation :builtin/icon [{:keys [x1 y1 rotation view-box] :or {rotation 0} :as shape}] @@ -301,6 +307,18 @@ (gmt/rotate rotation) (gmt/translate (- center-x) (- center-y))))) + +(defmethod -transformation :builtin/text + [{:keys [x1 y1 rotation] :or {rotation 0} :as shape}] + (let [{:keys [width height]} (size shape) + center-x (+ x1 (/ width 2)) + center-y (+ y1 (/ height 2))] + (-> (gmt/matrix) + (gmt/translate center-x center-y) + (gmt/rotate rotation) + (gmt/translate (- center-x) (- center-y))))) + + (defmethod -transformation :builtin/circle [{:keys [cx cy rx ry rotation] :or {rotation 0} :as shape}] (-> (gmt/matrix) @@ -325,7 +343,7 @@ (defmethod -transformation :default [shape _] - (throw (ex-info "Not implemented" (select-keys shape [:type])))) + (throw (ex-info "Not implemented (-transformation)" (select-keys shape [:type])))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helpers diff --git a/src/uxbox/ui/mixins.cljs b/src/uxbox/ui/mixins.cljs index 1e484bc13..60473b430 100644 --- a/src/uxbox/ui/mixins.cljs +++ b/src/uxbox/ui/mixins.cljs @@ -26,6 +26,12 @@ (rum/element cls state nil))))] (with-meta ctr {:rum/class cls}))) +(defn ref-html + [own ref] + (let [component (-> own :rum/react-component) + node (aget (.-refs component) ref)] + (.-innerHTML node))) + (defn ref-value [own ref] (let [component (-> own :rum/react-component) diff --git a/src/uxbox/ui/shapes.cljs b/src/uxbox/ui/shapes.cljs index d8496630a..e45178acf 100644 --- a/src/uxbox/ui/shapes.cljs +++ b/src/uxbox/ui/shapes.cljs @@ -1,6 +1,7 @@ (ns uxbox.ui.shapes "A ui related implementation for uxbox.shapes ns." (:require [uxbox.ui.shapes.core :as usc] + [uxbox.ui.shapes.text] [uxbox.ui.shapes.icon] [uxbox.ui.shapes.rect] [uxbox.ui.shapes.group] diff --git a/src/uxbox/ui/shapes/text.cljs b/src/uxbox/ui/shapes/text.cljs new file mode 100644 index 000000000..fd1d53eb9 --- /dev/null +++ b/src/uxbox/ui/shapes/text.cljs @@ -0,0 +1,147 @@ +(ns uxbox.ui.shapes.text + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [lentes.core :as l] + [goog.events :as events] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.mixins :as mx] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.ui.shapes.icon :as uusi] + [uxbox.util.dom :as dom]) + (:import goog.events.EventType)) + +(defn on-mouse-down + [event own {:keys [id group] :as shape} selected] + (let [selected? (contains? selected id) + local (:rum/local own)] + (when-not (:blocked shape) + (cond + (:edition @local) + nil + + (and group (:locked (ush/resolve-parent shape))) + nil + + (and (not selected?) (empty? selected)) + (do + (dom/stop-propagation event) + (uuc/emit-action! :shape/movement) + (rs/emit! (dw/select-shape id))) + + (and (not selected?) (not (empty? selected))) + (do + (dom/stop-propagation event) + (if (kbd/shift? event) + (rs/emit! (dw/select-shape id)) + (rs/emit! (dw/deselect-all) + (dw/select-shape id)))) + + :else + (do + (dom/stop-propagation event) + (uuc/emit-action! :shape/movement)))))) + +(defn on-mouse-up + [event {:keys [id group] :as shape}] + (cond + (and group (:locked (ush/resolve-parent shape))) + nil + + :else + (do + (dom/stop-propagation event) + (uuc/emit-action! :nothing)))) + +(defn- text-component-did-mount + [own] + (letfn [(on-double-click [ev] + (let [container (mx/get-ref-dom own "container") + local (:rum/local own)] + (swap! local assoc :edition true) + (set! (.-contentEditable container) true) + (.setAttribute container "contenteditable" "true") + (.focus container))) + (on-blur [ev] + (let [container (mx/get-ref-dom own "container") + local (:rum/local own)] + (swap! local assoc :edition false) + (set! (.-contentEditable container) false) + (.removeAttribute container "contenteditable")))] + + (let [dom (mx/get-ref-dom own "main") + dom2 (mx/get-ref-dom own "container") + key1 (events/listen dom EventType.DBLCLICK on-double-click) + key2 (events/listen dom2 EventType.BLUR on-blur)] + (assoc own ::key1 key1)))) + +(defn- text-component-will-unmount + [own] + (let [key1 (::key1 own) + key2 (::key2 own)] + (events/unlistenByKey key1) + (events/unlistenByKey key2) + (dissoc own ::key1 ::key2))) + +(defn- text-component-transfer-state + [old-own own] + (let [data (select-keys old-own [::key1 ::key2])] + (merge own data))) + +(defn- text-component-render + [own shape] + (let [{:keys [id x1 y1 content group]} shape + selected (rum/react uusc/selected-shapes-l) + selected? (and (contains? selected id) (= (count selected) 1)) + on-mouse-down #(on-mouse-down % own shape selected) + on-mouse-up #(on-mouse-up % shape) + local (:rum/local own)] + (html + [:g.shape {:class (when selected? "selected") + ;; :on-double-click #(on-double-click own %) + :ref "main" + :on-mouse-down on-mouse-down + :on-mouse-up on-mouse-up} + (uusc/render-shape (assoc shape :editing? (:edition @local false)) nil) + (when (and selected? (not (:edition @local false))) + (uusi/handlers shape))]))) + +(def ^:const text-component + (mx/component + {:render text-component-render + :name "text-componet" + :did-mount text-component-did-mount + :will-unmount text-component-will-unmount + :transfer-state text-component-transfer-state + :mixins [mx/static rum/reactive (mx/local)]})) + +(defmethod uusc/render-component :builtin/text + [own shape] + (text-component shape)) + +(def ^:const +select-rect-attrs+ + {:stroke-dasharray "5,5" + :style {:stroke "#333" :fill "transparent" + :stroke-opacity "1"}}) + +(defmethod uusc/render-shape :builtin/text + [{:keys [id x1 y1 x2 y2 content drawing? editing?] :as shape}] + (let [key (str id) + rfm (ush/-transformation shape) + size (ush/size shape) + props {:x x1 :y y1 + :transform (str rfm)} + attrs (merge props size)] + (html + [:g + (if (or drawing? editing?) + [:g + [:rect (merge attrs +select-rect-attrs+)]]) + [:foreignObject attrs + [:p {:ref "container"} content]]]))) + diff --git a/src/uxbox/ui/workspace.cljs b/src/uxbox/ui/workspace.cljs index 88af583b9..14020481b 100644 --- a/src/uxbox/ui/workspace.cljs +++ b/src/uxbox/ui/workspace.cljs @@ -32,11 +32,11 @@ (defn- on-key-down [event] - (js/console.log event)) + #_(js/console.log event)) (defn- on-key-up [event] - (js/console.log event)) + #_(js/console.log event)) (defn- workspace-render [own projectid] diff --git a/src/uxbox/ui/workspace/canvas/draw.cljs b/src/uxbox/ui/workspace/canvas/draw.cljs index 5efb5b007..bf88e7c88 100644 --- a/src/uxbox/ui/workspace/canvas/draw.cljs +++ b/src/uxbox/ui/workspace/canvas/draw.cljs @@ -26,7 +26,8 @@ (let [shape (rum/react +drawing-shape+) position (rum/react +drawing-position+)] (when shape - (-> (ush/resize shape position) + (-> (assoc shape :drawing? true) + (ush/resize position) (uusc/render-shape identity))))) (def ^:static draw-area @@ -79,6 +80,7 @@ (when-let [shape (:drawing @wb/workspace-l)] (case (:type shape) :builtin/icon (init-icon shape) + :builtin/text (init-shape shape) :builtin/rect (init-shape shape) :builtin/circle (init-shape shape) :builtin/line (init-shape shape))))] diff --git a/src/uxbox/ui/workspace/sidebar/drawtools.cljs b/src/uxbox/ui/workspace/sidebar/drawtools.cljs index ecc59f25b..20eb9cc65 100644 --- a/src/uxbox/ui/workspace/sidebar/drawtools.cljs +++ b/src/uxbox/ui/workspace/sidebar/drawtools.cljs @@ -31,27 +31,34 @@ ;; Draw Tools ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(def ^:staric +draw-tools+ +(def ^:const +draw-tools+ {:rect {:icon i/box :help (tr "ds.help.rect") :shape {:type :builtin/rect :name "Rect" :stroke "#000000"} - :priority 10} + :priority 1} :circle {:icon i/circle :help (tr "ds.help.circle") :shape {:type :builtin/circle :name "Circle"} - :priority 20} + :priority 2} :line {:icon i/line :help (tr "ds.help.line") :shape {:type :builtin/line :name "Line" :stroke "#000000"} - :priority 30}}) + :priority 3} + :text + {:icon i/text + :help (tr "ds.help.text") + :shape {:type :builtin/text + :name "Text" + :content "Hello world"} + :priority 4}}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Draw Tool Box diff --git a/src/uxbox/ui/workspace/sidebar/layers.cljs b/src/uxbox/ui/workspace/sidebar/layers.cljs index d503cd7d7..ece39ca3e 100644 --- a/src/uxbox/ui/workspace/sidebar/layers.cljs +++ b/src/uxbox/ui/workspace/sidebar/layers.cljs @@ -90,6 +90,7 @@ :builtin/line i/line :builtin/circle i/circle :builtin/rect i/box + :builtin/text i/text :builtin/group i/folder))