Skip to content

Instantly share code, notes, and snippets.

@arsane
Created September 22, 2015 09:41
Show Gist options
  • Save arsane/fabb2f20155d04ec8505 to your computer and use it in GitHub Desktop.
Save arsane/fabb2f20155d04ec8505 to your computer and use it in GitHub Desktop.
;;;; http://stuartsierra.com/2010/01/05/taming-the-gridbaglayout
(import '(javax.swing JFrame JPanel JButton JOptionPane JLabel))
(import 'java.awt.event.ActionListener)
(import '(java.awt GridBagConstraints GridBagLayout Insets))
;;;
(comment
(def button (JButton. "Click Me!"))
(def panel (doto (JPanel.)
(.add button)))
(def frame (doto (JFrame. "Hello Frame")
(.setSize 200 200)
(.setContentPane panel)
(.setVisible true)))
(defn say-hello []
(JOptionPane/showMessageDialog
nil "Hello, World!" "Greeting"
JOptionPane/INFORMATION_MESSAGE))
(def act (proxy [ActionListener] []
(actionPerformed [event] (say-hello))))
;(.addActionListener button act)
(.addActionListener (doto button
(on-action event
(say-hello))))
)
;;; define on-action macro
(defmacro on-action [component event & body]
`(. ~component addActionListener
(proxy [java.awt.event.ActionListener] []
(actionPerformed [~event] ~@body))))
;;; imperative version
(defn counter-app1 []
(let [label (JLabel. "Counter: 0")
button (JButton. "Add 1")
panel (JPanel.)
frame (JFrame. "Counter App")]
(.setOpaque panel true)
(.add panel label)
(.add panel button)
(.setContentPane frame panel)
(.setSize frame 300 100)
(.setVisible frame true)))
;;; better version.
(defn counter-app2 []
(let [label (JLabel. "Counter: 0")
button (JButton. "Add 1")
panel (doto (JPanel.)
(.setOpaque true)
(.add label)
(.add button))]
(doto (JFrame. "Counter App")
(.setContentPane panel)
(.setSize 300 100)
(.setVisible true))))
;;; with macro
(defn counter-app []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
button (doto (JButton. "Add 1")
(on-action evnt ;; evnt is not used
(.setText label
(str "Counter: " (swap! counter inc)))))
panel (doto (JPanel.)
(.setOpaque true)
(.add label)
(.add button))]
(doto (JFrame. "Counter App")
(.setContentPane panel)
(.setSize 300 100)
(.setVisible true))))
;;; Without set-grid! macro:
;;;
;;; (def c (GridBagConstraints.))
;;; (set! (. c gridx) 1)
;;; (set! (. c gridy) GridBagConstraints/RELATIVE)
;;;
;;; if the value of a field is a keyword, it gets used as the name
;;; of a static field (constant) in GridBagConstraints.
;;;
;;; With set-grid! macro:
;;;
;;; (set-grid! c :gridx 1)
;;; (set-grid! c :gridy :RELATIVE)
;;;
(defmacro set-grid! [constraints field value]
`(set! (. ~constraints ~(symbol (name field)))
~(if (keyword? value)
`(. java.awt.GridBagConstraints
~(symbol (name value)))
value)))
;;;
;;; (grid-bag-layout container
;;; :gridx 0, :gridy 0
;;; component-one
;;; :gridx :RELATIVE, :gridwidth 2
;;; component-two
;;; ;; ... more components & constraints ...
;;; )
(defmacro grid-bag-layout [container & body]
(let [c (gensym "c")
cntr (gensym "cntr")]
`(let [~c (new java.awt.GridBagConstraints)
~cntr ~container]
~@(loop [result '() body body]
(if (empty? body)
(reverse result)
(let [expr (first body)]
(if (keyword? expr)
(recur (cons `(set-grid! ~c ~expr
~(second body))
result)
(next (next body)))
(recur (cons `(.add ~cntr ~expr ~c)
result)
(next body)))))))))
;;; With layout manager
(defn counter-app3 []
(def panel
(doto (JPanel. (GridBagLayout.))
(grid-bag-layout
:fill :BOTH, :insets (Insets. 5 5 5 5)
:gridx 0, :gridy 0
(JButton. "One")
:gridy 1
(JButton. "Two")
:gridx 1, :gridy 0, :gridheight 2
(JButton. "Three"))))
(def frame
(doto (JFrame. "GridBagLayout Test")
(.setContentPane panel)
(.pack)
(.setVisible true))))
;;; program
(counter-app)
;;;;
(import '(javax.swing JLabel JPanel JFrame JTextField)
'(javax.swing.event DocumentListener)
'(java.awt GridBagLayout GridBagConstraints Insets))
(defmacro set-grid! [constraints field value]
`(set! (. ~constraints ~(symbol (name field)))
~(if (keyword? value)
`(. GridBagConstraints ~(symbol (name value)))
value)))
(defmacro grid-bag-layout [container & body]
(let [c (gensym "c")
cntr (gensym "cntr")]
`(let [~c (new java.awt.GridBagConstraints)
~cntr ~container]
~@(loop [result '() body body]
(if (empty? body)
(reverse result)
(let [expr (first body)]
(if (keyword? expr)
(recur (cons `(set-grid! ~c ~expr
~(second body))
result)
(next (next body)))
(recur (cons `(.add ~cntr ~expr ~c)
result)
(next body)))))))))
(defn f-to-c [f]
(* (- f 32) 5/9))
(defn c-to-f [c]
(+ (* c 9/5) 32))
;;; take a String typed by the user and convert it to a number
(defn parse [s]
(try (Double/parseDouble (.trim s))
(catch NumberFormatException e nil)))
;;; takes a number, rounds it to the nearest Integer, and
;;; return a String.
(defn display [n]
(str (Math/round (float n))))
;;; update target's temp
(defn update-temp [source target convert]
(when (.isFocusOwner source)
(if-let [n (parse (.getText source))]
(.setText target (display (convert n)))
(.setText target ""))))
(defn listen-temp [source target f]
(.. source getDocument
(addDocumentListener
(proxy [DocumentListener] []
(insertUpdate [e] (update-temp source target f))
(removeUpdate [e] (update-temp source target f))
(changedUpdate [e] )))))
(defn temp-app []
(let [celsius (JTextField. 3)
fahrenheit (JTextField. 3)
panel (doto (JPanel. (GridBagLayout.))
(grid-bag-layout
:gridx 0, :gridy 0, :anchor :LINE_END
:insets (Insets. 5 5 5 5)
(JLabel. "Degrees Celsius:")
:gridy 1
(JLabel. "Degrees Fahrenheit:")
:gridx 1, :gridy 0, :anchor :LINE_START
celsius
:gridy 1
fahrenheit))]
(listen-temp celsius fahrenheit c-to-f)
(listen-temp fahrenheit celsius f-to-c)
(doto (JFrame. "Temperature Converter")
(.setContentPane panel)
(.pack)
(.setVisible true))))
(temp-app)
(import '(java.awt GridBagLayout Insets))
(import '(javax.swing JPanel JFrame JButton JTextField
JLabel Timer SwingUtilities))
;;; create a flipper agent
(defn new-flipper []
(agent {:total 0, :heads 0,
:running false,
:random (java.util.Random.)}))
;;; - call random number generator and update counters
;;; - send itself to the agent again, creating a loop
(defn calculate [state]
(if (:running state)
(do (send *agent* calculate)
(assoc state
:total (inc (:total state))
:heads (if (.nextBoolean (:random state))
(inc (:heads state))
(:heads state))))
state))
;;; start agent
(defn start [state]
(send *agent* calculate)
(assoc state :running true))
;;; stop agent
(defn stop [state]
(assoc state :running false))
;;; how far from 0.5
(defn error [state]
(if (zero? (:total state)) 0.0
(- (/ (double (:heads state))
(:total state))
0.5)))
(defn text-field [value]
(doto (JTextField. value 15)
(.setEnabled false)
(.setHorizontalAlignment JTextField/RIGHT)))
(defmacro with-action [component event & body]
`(. ~component addActionListener
(proxy [java.awt.event.ActionListener] []
(actionPerformed [~event] ~@body))))
(defmacro set-grid! [constraints field value]
`(set! (. ~constraints ~(symbol (name field)))
~(if (keyword? value)
`(. java.awt.GridBagConstraints
~(symbol (name value)))
value)))
(defmacro grid-bag-layout [container & body]
(let [c (gensym "c")
cntr (gensym "cntr")]
`(let [~c (new java.awt.GridBagConstraints)
~cntr ~container]
~@(loop [result '() body body]
(if (empty? body)
(reverse result)
(let [expr (first body)]
(if (keyword? expr)
(recur (cons `(set-grid! ~c ~expr
~(second body))
result)
(next (next body)))
(recur (cons `(.add ~cntr ~expr ~c)
result)
(next body)))))))))
(defn flipper-app []
;; Construct components:
(let [flipper (new-flipper)
b-start (JButton. "Start")
b-stop (doto (JButton. "Stop")
(.setEnabled false))
total (text-field "0")
heads (text-field "0")
t-error (text-field "0.0")
timer (Timer. 100 nil)]
;; Setup actions:
(with-action timer e
(let [state @flipper]
(.setText total (str (:total state)))
(.setText heads (str (:heads state)))
(.setText t-error (format "%.10g" (error state)))))
(with-action b-start e
(send flipper start)
(.setEnabled b-stop true)
(.setEnabled b-start false)
(.start timer))
(with-action b-stop e
(send flipper stop)
(.setEnabled b-stop false)
(.setEnabled b-start true)
(.stop timer))
;; Create window and layout:
(doto (JFrame. "Flipper")
(.setContentPane
(doto (JPanel.)
(.add (JLabel. "Total:"))
(.add total)
(.add (JLabel. "Heads:"))
(.add heads)
(.add (JLabel. "Error:"))
(.add t-error)
(.add b-start)
(.add b-stop)))
(.addWindowListener
(proxy [java.awt.event.WindowListener] []
(windowActivated [e])
(windowClosed [e])
(windowClosing [e]
(send flipper stop)
(.stop timer))
(windowDeactivated [e])
(windowDeiconified [e])
(windowIconified [e])
(windowOpened [e])))
(.pack)
(.setVisible true))))
(defn flipper-app2 []
;; Construct components:
(let [flipper (new-flipper)
b-start (JButton. "Start")
b-stop (doto (JButton. "Stop")
(.setEnabled false))
total (text-field "0")
heads (text-field "0")
t-error (text-field "0.0")
timer (Timer. 100 nil)]
;; Setup actions:
(with-action timer e
(let [state @flipper]
(.setText total (str (:total state)))
(.setText heads (str (:heads state)))
(.setText t-error (format "%.10g" (error state)))))
(with-action b-start e
(send flipper start)
(.setEnabled b-stop true)
(.setEnabled b-start false)
(.start timer))
(with-action b-stop e
(send flipper stop)
(.setEnabled b-stop false)
(.setEnabled b-start true)
(.stop timer))
;; Create window and layout:
(doto (JFrame. "Flipper")
(.setContentPane
(doto (JPanel. (GridBagLayout.))
(grid-bag-layout
:insets (Insets. 5 5 5 5)
:gridx 0, :anchor :LINE_END
:gridy 0, (JLabel. "Total:")
:gridy 1, (JLabel. "Heads:")
:gridy 2, (JLabel. "Error:")
:gridx 1, :anchor :LINE_START
:gridy 0, total
:gridy 1, heads
:gridy 2, t-error
:gridx 0, :gridy 3, :gridwidth 2, :anchor :CENTER
(doto (JPanel.)
(.add b-start)
(.add b-stop)))))
(.pack)
(.setVisible true))))
(SwingUtilities/invokeLater flipper-app2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment