From f870e0a918a1ed257cba1cf55d8ca03d9b89767c Mon Sep 17 00:00:00 2001 From: vlaaad Date: Wed, 24 Mar 2021 09:02:08 +0100 Subject: [PATCH] Auto-insert matching brackets --- src/vlaaad/reveal/action_popup.clj | 1 + src/vlaaad/reveal/fx.clj | 54 ++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/vlaaad/reveal/action_popup.clj b/src/vlaaad/reveal/action_popup.clj index d36209c..f223938 100644 --- a/src/vlaaad/reveal/action_popup.clj +++ b/src/vlaaad/reveal/action_popup.clj @@ -178,6 +178,7 @@ :on-cancel on-cancel :desc {:fx/type fx/ext-let-refs :refs (into {::text-field {:fx/type :text-field + :text-formatter rfx/code-text-formatter :style-class "reveal-text-field" :text text :on-focused-changed {::event/type ::on-text-focused diff --git a/src/vlaaad/reveal/fx.clj b/src/vlaaad/reveal/fx.clj index 9881740..ad8cefb 100644 --- a/src/vlaaad/reveal/fx.clj +++ b/src/vlaaad/reveal/fx.clj @@ -1,7 +1,9 @@ (ns vlaaad.reveal.fx (:require [cljfx.lifecycle :as fx.lifecycle] [cljfx.component :as fx.component]) - (:import [java.util UUID])) + (:import [java.util UUID] + [java.util.function UnaryOperator] + [javafx.scene.control TextFormatter$Change])) (def ext-with-process "Extension lifecycle that provides \"local mutable state\" capability @@ -43,4 +45,52 @@ (delete [_ component opts] (when-let [stop-fn (:stop-fn component)] (stop-fn)) - (fx.lifecycle/delete fx.lifecycle/dynamic (:child component) opts)))) \ No newline at end of file + (fx.lifecycle/delete fx.lifecycle/dynamic (:child component) opts)))) + +(defn- format! [^TextFormatter$Change change] + (when (.isContentChange change) + (let [range-start (.getRangeStart change) + range-end (.getRangeEnd change) + control-text (.getControlText change) + inputs-char (and (pos? range-start) + (= \\ (.charAt control-text (dec range-start)))) + move-right? (fn [char] + (and (not (.isDeleted change)) + (< range-end (.length control-text)) + (= char (.charAt control-text range-end)))) + move-right! (fn [] + (.setText change "") + (.selectRange change (inc range-end) (inc range-end)))] + (cond + (not (.isAdded change)) + (when (and (= 1 (- range-end range-start)) + (< range-end (.length control-text))) + (let [deleted-char (.charAt control-text range-start) + next-char (.charAt control-text range-end)] + (case [deleted-char next-char] + ([\" \"] [\{ \}] [\[ \]] [\( \)]) + (.setRange change range-start (inc range-end)) + nil))) + + inputs-char + nil + + :else + (case (.getText change) + "\"" (if (move-right? \") (move-right!) (.setText change "\"\"")) + "[" (.setText change "[]") + "]" (when (move-right? \]) (move-right!)) + "{" (.setText change "{}") + "}" (when (move-right? \}) (move-right!)) + "(" (.setText change "()") + ")" (when (move-right? \)) (move-right!)) + nil))))) + +(def code-text-formatter-filter + (reify UnaryOperator + (apply [_ v] + (doto v format!)))) + +(def code-text-formatter + {:fx/type :text-formatter + :filter code-text-formatter-filter}) \ No newline at end of file