2

我正在开发一个 Clojure 宏来帮助构建基于 GridBagLayout 的 JPanel。我可以在宏内部的默认映射中获取 Java 类以进行命名空间限定,但不能将那些作为参数传入的类。我需要什么反引号、引号、tildas 或其他东西的神奇组合?

(import [java.awt GridBagConstraints GridBagLayout Insets]
        [javax.swing JButton JPanel])

(defmacro make-constraints [gridx gridy & constraints]
  (let [defaults
        {:gridwidth 1 :gridheight 1 :weightx 0 :weighty 0
         :anchor 'GridBagConstraints/WEST :fill 'GridBagConstraints/NONE
         :insets `(Insets. 5 5 5 5) :ipadx 0 :ipady 0}

        values
        (assoc (merge defaults (apply hash-map constraints))
          :gridx gridx :gridy gridy)]
    `(GridBagConstraints. ~@(map (fn [value]
                                   (if
                                    (or
                                     (number? value)
                                     (string? value)
                                     (char? value)
                                     (true? value)
                                     (false? value)
                                     (nil? value))
                                    value
                                    `~value))
                                 (map values
                                      [:gridx :gridy :gridwidth :gridheight
                                       :weightx :weighty :anchor :fill
                                       :insets :ipadx :ipady])))))

当我使用Insets默认映射中的定义时,它被限定为(不是“符号捕获”)(java.awt.Insets ...)

user=> (macroexpand-1 '(make-constraints 0 0 :weightx 1))
(java.awt.GridBagConstraints.
 0 0 1 1 1 0
 GridBagConstraints/WEST GridBagConstraints/NONE
 (java.awt.Insets. 5 5 5 5) 0 0)

但是当我将它作为参数传递时,它不会:

user=> (macroexpand-1 '(make-constraints 1 1 :insets (Insets. 2 2 2 2)))
(java.awt.GridBagConstraints.
 1 1 1 1 0 0
 GridBagConstraints/WEST GridBagConstraints/NONE
 (Insets. 2 2 2 2) 0 0)

我不只是想成为一个坚持不懈的人。我收到编译器错误,它找不到合适的GridBagConstraints构造函数。

4

2 回答 2

3

我不知道GridBagLayout,但以下内容基本上应该与您的宏类似。如果您有一个:height大于 1 的组件,则必须nil在其下方添加列以保持列计数器同步。假设您arrive-text-field的高度为 2,而您必须在该行nil之前添加一行depart-label以保持计数器正确。这只是一个快速破解。

(def default-opts
  {:insets   (Insets. 0 0 0 0)
   :width    1
   :height   1
   :weight-x 0.0
   :weight-y 0.0
   :fill     GridBagConstraints/NONE
   :anchor   GridBagConstraints/WEST
   :ipadx    0
   :ipady    0})

(defn grid-bag-constraints
  [x y global-opts opts]
  (let [{:keys [insets width height weight-x weight-h
                fill anchor ipadx ipady]}
        (merge default-opts global-opts opts)]
    (GridBagConstraints. x y width height weight-x weight-h
                         anchor fill insets ipadx ipady)))

(defn grid-bag-container
  [panel global-opts & rows]
  (doseq [[row-idx row] (map-indexed identity rows)
          [col-idx [target & {:as opts}]] (map-indexed identity row)
          :when target]
    (let [constraints (grid-bag-constraints col-idx row-idx global-opts opts)]
      (.add panel target constraints))))

用法和以前一样。

于 2011-07-01T13:09:15.080 回答
1

这是我的解决方案。我在我正在编写的 Swing 应用程序中使用它。它已经为我节省了很多行代码编写(用于两个不同的面板),并且与手写代码一样快。

(defmacro grid-bag-container [container & args]
  "Fill and return a java.awt.Container that uses the GridBagLayout.
  The macro defines a set of default constraints for the GridBagConstraints:
    :gridwidth 1
    :gridheight 1
    :weightx 0
    :weighty 0
    :anchor :WEST
    :fill :NONE
    :insets (Insets. 5 5 5 5)
    :ipadx 0
    :ipady 0
  These defaults can be overridden in the call to the macro in two way:
    - If the first argument is a hash-map of constraint names and values
      (e.g.: {:weightx 1}), these will override the defaults for the
      entire container.
    - Each individual item (see below) can override the global defaults
      and container defaults for itself.
  The constraints consist of constraint name (as a keyword with the same
  name as the GridBagConstraints field), and a value, which can also be
  a keyword, in which case the appropriate constant from GridBagConstraints
  will be substituted (e.g.: :NONE == GridBagConstraints.NONE), or the value
  can be an expression (e.g.: 0 or (Insets. 2 2 2 2)).
  Following the optional container default overrides hash-map are one or
  more row specification vectors. Each vector represents one row and
  increments gridy (starting from 0). Each vector contains one or more
  item vectors representing the individual components to be added to the
  container. Each item vector has the component as its first value,
  followed by zero or more constraint overrides as keyword-value pairs.
  (e.g.: [myButton :gridwidth 2 :weightx 1]). The values may be keywords
  and are expanded to GridBagConstraints constants as described above.
  Each item vector gets the next value of gridx (starting with 0) in that
  row.
  For example:
    (grid-bag-container panel
      {:insets (Insets. 1 1 1 1)}
      [[button :gridwidth 2 :weightx 1.0 :fill :HORIZONTAL]]
      [[check-box :gridwidth 2 :weightx 1.0 :anchor :CENTER]]
      [[arrive-label] [arrive-text-field :fill :HORIZONTAL]]
      [[depart-label] [depart-text-field :fill :HORIZONTAL]])
  will expand to the hand-written equivalent:
    (doto panel
      (.add button
        (GridBagConstraints. 0 0 2 1 1.0 0  ; gridx: 0 gridy: 1
                             GridBagConstraints/WEST
                             GridBagConstraints/HORIZONTAL
                             (Insets. 1 1 1 1) 0 0))
      (.add check-box
        (GridBagConstraints. 0 1 2 1 1.0 0  ; gridx: 0 gridy: 1
                             GridBagConstraints/CENTER
                             GridBagConstraints/NONE
                             (Insets. 1 1 1 1) 0 0))
      (.add arrive-label
        (GridBagConstraints. 0 2 1 1 0 0    ; gridx: 0 gridy: 2
                             GridBagConstraints/WEST
                             GridBagConstraints/NONE
                             (Insets. 1 1 1 1) 0 0))
      (.add arrive-text-field
        (GridBagConstraints. 1 2 1 1 0 0    ; gridx: 1 gridy: 2
                             GridBagConstraints/WEST
                             GridBagConstraints/HORIZONTAL
                             (Insets. 1 1 1 1) 0 0))
      (.add depart-label
        (GridBagConstraints. 0 3 1 1 0 0    ; gridx: 0 gridy: 3
                             GridBagConstraints/WEST
                             GridBagConstraints/NONE
                             (Insets. 1 1 1 1) 0 0))
      (.add depart-text-field
        (GridBagConstraints. 1 3 1 1 0 0    ; gridx: 1 gridy: 3
                             GridBagConstraints/WEST
                             GridBagConstraints/HORIZONTAL
                             (Insets. 1 1 1 1) 0 0))
  @param container the java.awt.Container to fill
  @param args the components and GridBagContraints speicifcations
  @returns the filled Container"
  (let [global-defaults
        {:gridwidth 1
         :gridheight 1
         :weightx 0
         :weighty 0
         :anchor :WEST
         :fill :NONE
         :insets `(Insets. 5 5 5 5)
         :ipadx 0
         :ipady 0}

        [defaults rows]
        (if (map? (first args))
          [(into global-defaults (first args)) (rest args)]
          [global-defaults args])]
    `(doto ~container
      ~@(loop [gridy 0 rows rows ret []]
        (if (seq rows)
          (recur (inc gridy) (rest rows)
            (into ret
              (let [row (first rows)]
                (loop [gridx 0 row row ret []]
                  (if (seq row)
                    (recur (inc gridx) (rest row)
                      (conj ret
                        (let [item
                              (first row)

                              component
                              (first item)

                              constraints
                              (assoc (merge defaults
                                            (apply hash-map (rest item)))
                                :gridx gridx :gridy gridy)

                              constraint-values
                              (map (fn [value]
                                (if (keyword? value)
                                  `(. GridBagConstraints
                                      ~(symbol (name value)))
                                  `~value))
                                (map constraints
                                  [:gridx :gridy :gridwidth :gridheight
                                   :weightx :weighty :anchor :fill
                                   :insets :ipadx :ipady]))]
                          `(.add ~component (new GridBagConstraints
                                                 ~@constraint-values)))))
                    ret)))))
          ret)))))

感谢amalloyuser100464kotarak的帮助。

于 2011-07-01T12:41:29.550 回答