Permalink
Browse files

Finished off map-key function, tests and docs.

  • Loading branch information...
1 parent 4b2f8cf commit b006831db106cb4a4bfe33cbe8472e4845fdd809 @daveray committed Dec 20, 2011
Showing with 87 additions and 10 deletions.
  1. +66 −8 src/seesaw/keymap.clj
  2. +21 −2 test/seesaw/test/keymap.clj
View
@@ -13,10 +13,13 @@
seesaw.keymap
(:use [seesaw.util :only [illegal-argument]]
[seesaw.keystroke :only [keystroke]]
- [seesaw.action :only [action]]))
+ [seesaw.action :only [action]]
+ [seesaw.to-widget :only [to-widget*]]))
(defn- ^javax.swing.Action to-action [act]
(cond
+ (nil? act) nil
+
(instance? javax.swing.Action act) act
(instance? javax.swing.AbstractButton act)
@@ -35,21 +38,76 @@
(def ^{:private true} scope-table
{ :descendants javax.swing.JComponent/WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
- :local javax.swing.JComponent/WHEN_FOCUSED
+ :self javax.swing.JComponent/WHEN_FOCUSED
:global javax.swing.JComponent/WHEN_IN_FOCUSED_WINDOW })
(def ^{:private true} default-scope (:descendants scope-table))
(defn map-key
- [target key act & {:keys [scope] :as opts}]
- (let [target (to-target target)
+ "Install a key mapping on a widget.
+
+ Key mappings are hopelessly entwined with keyboard focus and the widget
+ hierarchy. When a key is pressed in a widget with focus, each widget up
+ the hierarchy gets a chance to handle it. There three 'scopes' with
+ which a mapping may be registered:
+
+ :self
+
+ The mapping only handles key presses when the widget itself has
+ the keyboard focus. Use this, for example, to install custom
+ key mappings in a text box.
+
+ :descendants
+
+ The mapping handles key presses when the widget itself or any
+ of its descendants has keyboard focus.
+
+ :global
+
+ The mapping handles key presses as long as the top-level window
+ containing the widget is active. This is what's used for menu
+ shortcuts and should be used for other app-wide mappings.
+
+ Given this, each mapping is installed on a particular widget along
+ with the desired keystroke and action to perform. The keystroke can
+ be any valid argument to (seesaw.keystroke/keystroke). The action
+ can be one of the following:
+
+ * A javax.swing.Action. See (seesaw.core/action)
+ * A single-argument function. An action will automatically be
+ created around it.
+ * A button, menu, menuitem, or other button-y thing. An action
+ that programmatically clicks the button will be created.
+ * nil to disable or remove a mapping
+
+ target may be a widget, frame, or something convertible through to-widget.
+
+ Returns a function that removes the key mapping.
+
+ Examples:
+
+ ; In frame f, key \"K\" clicks button b
+ (map-key f \"K\" b)
+
+ ; In text box t, map ctrl+enter to a function
+ (map-key t \"control ENTER\"
+ (fn [e] (alert e \"You pressed ctrl+enter!\")))
+
+ See:
+ (seesaw.keystroke/keystroke)
+ http://download.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
+ "
+ [target key act & {:keys [scope id] :as opts}]
+ (let [target (to-target (to-widget* target))
scope (scope-table scope default-scope)
im (.getInputMap target scope)
am (.getActionMap target)
act (to-action act)
- id act]
- (.put im (keystroke key) id)
+ id (or id act)
+ ks (keystroke key)]
+ (.put im ks id)
(.put am id act)
- ; TODO return value
- ))
+ (fn []
+ (.remove im ks)
+ (.remove am id))))
@@ -28,9 +28,10 @@
(let [b (button)
k (keystroke "A")
a (action)
- _ (map-key b k a :scope :local)
+ _ (map-key b k a :scope :self)
id (.. b (getInputMap javax.swing.JComponent/WHEN_FOCUSED) (get k))]
(expect (= a (.. b (getActionMap) (get id)))))))
+
(testing "a keystroke and a function"
(it "maps the key to an action that calls the function"
(let [b (button)
@@ -41,6 +42,18 @@
id (.. b (getInputMap javax.swing.JComponent/WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) (get k))]
(.. b (getActionMap) (get id) (actionPerformed nil))
(expect @called))))
+
+ (it "returns a function that undoes its effect"
+ (let [b (button)
+ k (keystroke "A")
+ called (atom 0)
+ a (fn [e] (swap! called inc))
+ remove-fn (map-key b k a)
+ id (.. b (getInputMap javax.swing.JComponent/WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) (get k))]
+ (expect (.. b (getActionMap) (get id)))
+ (remove-fn)
+ (expect (nil? (.. b (getActionMap) (get id))))))
+
(testing "a keystroke and a button"
(it "maps the key to .doClick on the button"
(let [k (keystroke "A")
@@ -49,5 +62,11 @@
_ (map-key b k b)
id (.. b (getInputMap javax.swing.JComponent/WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) (get k))]
(.. b (getActionMap) (get id) (actionPerformed nil))
- (expect @called)))))
+ (expect @called))))
+ (it "can assign an :id to a mapping"
+ (let [k (keystroke "A")
+ b (button)
+ _ (map-key b k b :id :foo :scope :global)
+ id (.. b (getInputMap javax.swing.JComponent/WHEN_IN_FOCUSED_WINDOW) (get k))]
+ (expect (= id :foo)))))

0 comments on commit b006831

Please sign in to comment.