iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Software Development

玩轉ABCL Lisp!用奇怪偏門的方式學習JavaFX系列 第 18 篇

lesson 9: CalcFX 畫面:用 ABCL + JavaFX 實現計算機畫面

  • 分享至 

  • xImage
  •  

TL;DR

這篇先使用 ABCL 與 JavaFX 建立 CalcFX 的計算機畫面,完成顯示器、按鈕排列與 CSS 風格設定。這裡只處理 GUI 畫面,按鈕的計算功能會在下一篇實作。

規劃計算機布局

計算機程式使用一個 4 x 5 的 Grid 布局,分成兩個部分:顯示器和按鈕區塊。Grid 每欄平分視窗寬度,所以每欄 25%,4 欄加總剛好 100%;第 0 列放置顯示器,不隨畫面變大;剩餘列放置按鈕,平均分配高度,每列 20% 的高度比例(以 0 開始計數)。

布局:

(grid (new "javafx.scene.layout.GridPane"))
(chain (#"getStyleClass" grid) ("add" "root-pane"))
(#"setVgap" grid 10.0d0) ; 垂直間距 10.0 (Java double)
(#"setHgap" grid 10.0d0) ; 水平間距 10.0 (Java double)

;; 共有 4 欄按鈕,每欄平均分配 25% 的寬度
(dotimes (i 4)
    (let ((col (new "javafx.scene.layout.ColumnConstraints")))
    (#"setPercentWidth" col 25.0d0)
    (#"add" (#"getColumnConstraints" grid) col)))

;; 第 0 列(顯示幕)不自動隨視窗長高,維持原高度
(let ((row0 (new "javafx.scene.layout.RowConstraints")))
    (#"setVgrow" row0 (jfield "javafx.scene.layout.Priority" "NEVER"))
    (#"add" (#"getRowConstraints" grid) row0))

;; 第 1 到 4 列(按鈕列)要平均隨視窗長高 (百分比填滿)
(dotimes (i 4)
    (let ((row (new "javafx.scene.layout.RowConstraints")))
    (#"setPercentHeight" row 20.0d0) ; 每列分配總高度的 20%
    (#"add" (#"getRowConstraints" grid) row)))

建立顯示器

顯示器是一個不可編輯的 TextField,占滿整個 Grid 的第 0 列。

顯示器:

(display (new "javafx.scene.control.TextField" "0"))
(chain (#"getStyleClass" display) ("add" "display"))

;; 設定顯示幕樣式與唯讀,並讓它寬度無限拉伸
(#"setEditable" display nil)
(#"setMaxWidth" display (jfield "java.lang.Double" "MAX_VALUE"))

;; 將顯示幕加入 GridPane (欄0, 列0, 跨4欄, 跨1列)
(#"add" grid display 0 0 4 1)

配置計算機按鈕

按鈕依照表格布局放到 Grid 對應的剩餘位置:

7 8 9 /
4 5 6 *
1 2 3 -
C 0 = +

按鈕又區分成數字按鈕和功能按鈕,目前兩者主要透過不同的 CSS 類別呈現風格差異。

按鈕:

;; 定義按鈕配置 (文字 . (欄 . 列))
(let ((buttons '(("7" . (0 . 1)) ("8" . (1 . 1)) ("9" . (2 . 1)) ("/" . (3 . 1))
                 ("4" . (0 . 2)) ("5" . (1 . 2)) ("6" . (2 . 2)) ("*" . (3 . 2))
                 ("1" . (0 . 3)) ("2" . (1 . 3)) ("3" . (2 . 3)) ("-" . (3 . 3))
                 ("C" . (0 . 4)) ("0" . (1 . 4)) ("=" . (2 . 4)) ("+" . (3 . 4)))))

    ;; 迴圈建立按鈕
    (dolist (btn-spec buttons)
        (let* ((label (car btn-spec))
                (col (cadr btn-spec))
                (row (cddr btn-spec))
                (btn (new "javafx.scene.control.Button" label))
                (btn-styles (#"getStyleClass" btn)))

            ;; 重要:移除 setPrefSize 固定值,改讓它大小無限拉伸以符合格子約束
            (#"setMaxSize" btn (jfield "java.lang.Double" "MAX_VALUE") (jfield "java.lang.Double" "MAX_VALUE"))

            ;; 根據功能套用不同 CSS 類別
            (#"add" btn-styles "calc-btn")
            (when (member label '("/" "*" "-" "+" "=") :test #'string=)
                (#"add" btn-styles "op-btn"))

            ;; 將按鈕加入面板
            (#"add" grid btn col row))))

載入 CalcFX CSS

設定使用的 CSS 風格檔案:

(let* ((scene (new "javafx.scene.Scene" grid 320.0d0 420.0d0))
       (stylesheets (#"getStylesheets" scene))
       (css-data-url (get-system-resource "res/calcfx.css")))
  (#"add" stylesheets css-data-url)
  scene)))

最後是 CSS 內容:

.root-pane { 
    -fx-background-color: #202020; 
    -fx-padding: 15; 
}
.display {
   -fx-background-color: #2d2d2d;
   -fx-text-fill: white;
   -fx-font-size: 28px;
   -fx-alignment: center-right;
   -fx-padding: 10;
   -fx-background-radius: 5;
}
.calc-btn {
   -fx-alignment: center;
   -fx-font-size: 20px;
   -fx-background-color: #3b3b3b;
   -fx-text-fill: white;
   -fx-background-radius: 5;
}
.calc-btn:hover { -fx-background-color: #4a4a4a; }
.op-btn { 
    -fx-background-color: #f1a33c; 
    -fx-text-fill: black;
}
.op-btn:hover { -fx-background-color: #f5b866; }

組合完整的 CalcFX 畫面

程式畫面與完整程式碼:

https://ithelp.ithome.com.tw/upload/images/20261001/20112470guYh2GrPOo.png

(defun create-calculator-scene ()
  (let* ((grid (new "javafx.scene.layout.GridPane"))
         (display (new "javafx.scene.control.TextField" "0")))  ;; 申明grid、display變數
    (chain (#"getStyleClass" grid) ("add" "root-pane"))
    (chain (#"getStyleClass" display) ("add" "display"))
    (#"setVgap" grid 10.0d0) ; 垂直間距 10.0 (Java double)
    (#"setHgap" grid 10.0d0) ; 水平間距 10.0 (Java double)
    (#"setEditable" display nil)
    (#"setMaxWidth" display (jfield "java.lang.Double" "MAX_VALUE"))

    ;; 共有 4 欄按鈕,每欄平均分配 25% 的寬度
    (dotimes (i 4)
      (let ((col (new "javafx.scene.layout.ColumnConstraints")))
        (#"setPercentWidth" col 25.0d0)
        (#"add" (#"getColumnConstraints" grid) col)))

    ;; 第 0 列(顯示幕)不自動隨視窗長高,維持原高度
    (let ((row0 (new "javafx.scene.layout.RowConstraints")))
      (#"setVgrow" row0 (jfield "javafx.scene.layout.Priority" "NEVER"))
      (#"add" (#"getRowConstraints" grid) row0))

    ;; 第 1 到 4 列(按鈕列)要平均隨視窗長高 (百分比填滿)
    (dotimes (i 4)
      (let ((row (new "javafx.scene.layout.RowConstraints")))
        (#"setPercentHeight" row 20.0d0) ; 每列分配總高度的 20%
        (#"add" (#"getRowConstraints" grid) row)))

    ;; 定義按鈕配置 (文字 . (欄 . 列))
    (#"add" grid display 0 0 4 1)
    (let ((buttons '(("7" . (0 . 1)) ("8" . (1 . 1)) ("9" . (2 . 1)) ("/" . (3 . 1))
                     ("4" . (0 . 2)) ("5" . (1 . 2)) ("6" . (2 . 2)) ("*" . (3 . 2))
                     ("1" . (0 . 3)) ("2" . (1 . 3)) ("3" . (2 . 3)) ("-" . (3 . 3))
                     ("C" . (0 . 4)) ("0" . (1 . 4)) ("=" . (2 . 4)) ("+" . (3 . 4)))))

      ;; 迴圈建立按鈕
      (dolist (btn-spec buttons)
        (let* ((label (car btn-spec))
               (col (cadr btn-spec))
               (row (cddr btn-spec))
               (btn (new "javafx.scene.control.Button" label))
               (btn-styles (#"getStyleClass" btn)))

          ;; 重要:移除 setPrefSize 固定值,改讓它大小無限拉伸以符合格子約束
          (#"setMaxSize" btn (jfield "java.lang.Double" "MAX_VALUE") (jfield "java.lang.Double" "MAX_VALUE"))

          ;; 根據功能套用不同 CSS 類別
          (#"add" btn-styles "calc-btn")
          (when (member label '("/" "*" "-" "+" "=") :test #'string=)
            (#"add" btn-styles "op-btn"))
          ;; 將按鈕加入面板
          (#"add" grid btn col row))))
    (let* ((scene (new "javafx.scene.Scene" grid 320.0d0 420.0d0))
           (stylesheets (#"getStylesheets" scene))
           (css-data-url (get-system-resource "res/calcfx.css")))
      (#"add" stylesheets css-data-url)
      scene)))

執行結果

目前的 CalcFX 已完成計算機的畫面設計:顯示器位於上方,數字按鈕與運算按鈕依照表格排列,並透過 CSS 區分不同類型的按鈕。

下一篇將在這個畫面上加入計算機的功能邏輯,讓按鈕真正能夠處理輸入、運算與結果顯示。


上一篇
lesson 8: 資料雙向綁定:Hello {Name}!發揮 ABCL 的威力
系列文
玩轉ABCL Lisp!用奇怪偏門的方式學習JavaFX 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言