Skip to content
This repository
tree: 75af04e5f4
Fetching contributors…

Cannot retrieve contributors at this time

file 119 lines (109 sloc) 5.017 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
(ns processing.core.applet
  (:use [processing.core :except (size)])
  (:import (javax.swing JFrame)
           (java.awt.event WindowListener)))

(defn- fix-mname
  "Changes :method-name to :methodName."
  [[mname fun]]
  (let [mname (name mname)
        mr (re-matcher #"\-[a-zA-z]" mname)
        replace-fn (comp #(.replaceFirst mr %) toupper #(.substring % 1))
        fixed-name (if-let [matched (re-find mr)]
                     (replace-fn matched)
                     mname)]
    [(keyword fixed-name) fun]))

(defmacro defapplet
  "Define an applet. Takes an app-name and a map of options."
  [app-name & opts]
  (let [options (assoc (apply hash-map opts) :name (str app-name))
        fns (dissoc options :name :title :size :key-pressed :key-released :mouse-pressed :mouse-released)
        fns (into {} (map (fn [[k v]] [k (if (symbol? v) `(var ~v) v)]) fns))
        fns (merge {:draw (fn [] nil)} fns)
        key-pressed-fn (or (:key-pressed options)
                           (fn [] nil))
        key-released-fn (or (:key-released options)
                            (fn [] nil))
        mouse-pressed-fn (or (:mouse-pressed options)
                             (fn [] nil))
        mouse-released-fn (or (:mouse-released options)
                              (fn [] nil))
        methods (into {} (map fix-mname fns))]
    `(def ~app-name
       (let [frame# (atom nil)
             state# (atom nil)
             prx# (proxy [processing.core.PApplet
                          clojure.lang.IMeta] []
                    (meta [] (assoc ~options :frame frame#))
                    (keyPressed
                      ([] (binding [*applet* ~'this
                                    *state* state#]
                            (~key-pressed-fn)))
                      ([e#]
                         (proxy-super keyPressed e#)))
                    (keyReleased
                      ([] (binding [*applet* ~'this
                                    *state* state#]
                            (~key-released-fn)))
                      ([e#]
                         (proxy-super keyReleased e#)))
                    (mousePressed
                      ([] (binding [*applet* ~'this
                                    *state* state#]
                            (~mouse-pressed-fn)))
                      ([e#]
                         (proxy-super mousePressed e#)))
                    (mouseReleased
                      ([] (binding [*applet* ~'this
                                    *state* state#]
                            (~mouse-released-fn)))
                      ([e#]
                         (proxy-super mouseReleased e#))))

             bound-meths# (reduce (fn [methods# [method-name# f#]]
                                    (assoc methods# (name method-name#)
                                           (fn [this# & args#]
                                             (binding [*applet* this#
                                                       *state* state#]
                                               (apply f# args#)))))
                                  {}
                                  ~methods)]
         (update-proxy prx# bound-meths#)
         prx#))))

(defn stop [applet]
  (let [closing-fn (fn []
                     (let [frame @(:frame (meta applet))]
                       (.destroy applet)
                       (doto frame
                         (.hide)
                         (.dispose))))]
    (javax.swing.SwingUtilities/invokeAndWait closing-fn)))

(defn run
  "Launches the applet. If given the flag :interactive, it won't exit
on clicking the close button - it will only dispose the window."
  [applet & interactive?]
  (.init applet)
  (let [m (.meta applet)
        [width height & _] (or (:size m) [200 200])
        close-op (if (first interactive?)
                   JFrame/DISPOSE_ON_CLOSE
                   JFrame/EXIT_ON_CLOSE)]
    (reset! (:frame m)
            (doto (JFrame. (or (:title m) (:name m)))
              (.addWindowListener (reify WindowListener
                                     (windowActivated [this e])
                                     (windowClosing [this e]
                                       (future (stop applet)))
                                     (windowDeactivated [this e])
                                     (windowDeiconified [this e])
                                     (windowIconified [this e])
                                     (windowOpened [this e])
                                     (windowClosed [this e])))
              (.setDefaultCloseOperation close-op)
              (.setSize width height)
              (.add applet)
              (.pack)
              (.show)))))




(comment ;; Usage:
  (defapplet growing-triangle
    :draw (fn [] (line 10 10 (frame-count) 100)))

  (run growing-triangle)
  (stop growing-triangle))
Something went wrong with that request. Please try again.