├── .gitignore
├── resources
├── icon.icns
├── screenshot.png
├── icon.iconset
│ ├── icon_16x16.png
│ ├── icon_32x32.png
│ ├── icon_128x128.png
│ ├── icon_16x16@2x.png
│ ├── icon_256x256.png
│ ├── icon_32x32@2x.png
│ ├── icon_512x512.png
│ ├── icon_128x128@2x.png
│ ├── icon_256x256@2x.png
│ └── icon_512x512@2x.png
├── folder.svg
└── file.svg
├── script
└── nrepl.sh
├── README.md
├── deps.edn
├── .clj-kondo
└── imports
│ └── io.github.humbleui
│ └── humbleui
│ ├── hooks
│ ├── deflazy.clj_kondo
│ ├── loopr.clj_kondo
│ └── condplus.clj_kondo
│ └── config.edn
├── src
└── file_picker
│ └── core.clj
├── step-by-step
├── 00_hello_world.clj
├── 01_rect_padding.clj
├── 02_column_row.clj
├── 03_gap_align.clj
├── 04_comp_state.clj
├── 05_file_list.clj
├── 06_selectable.clj
├── 07_hoverable.clj
├── 08_current_dir.clj
├── 09_go_down.clj
├── 10_go_up.clj
└── 11_icons.clj
└── humble-file-picker.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | .calva
2 | .clj-kondo/.cache
3 | .cpcache
4 | .idea
5 | .lsp
6 | .nrepl-port
7 | .repl-port
--------------------------------------------------------------------------------
/resources/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.icns
--------------------------------------------------------------------------------
/resources/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/screenshot.png
--------------------------------------------------------------------------------
/script/nrepl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit -o nounset -o pipefail
3 | cd `dirname $0`/..
4 |
5 | clj -M:dev
6 |
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/resources/icon.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HumbleUI/humble-file-picker/HEAD/resources/icon.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/resources/folder.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/resources/file.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Demo project for [Humble UI](https://github.com/HumbleUI/HumbleUI)
2 |
3 | 
4 |
5 | Start by running
6 |
7 | ```
8 | clj -M:dev
9 | ```
10 |
11 | Eval `(file-picker/-main)` to open main window.
12 |
13 | Replace `src/file_picker/core.clj` with any file from `step-by-step/` to see incremental progress.
14 |
15 | Eval `(io.github.humbleui.docs/open!)` to view Humble UI docs.
16 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:deps
2 | {org.clojure/clojure {:mvn/version "1.12.0"}
3 | io.github.humbleui/humbleui
4 | {:git/sha "0a5c7584493d2ceb0f431a3e3165f92668209ff0"}}
5 | :aliases
6 | {:dev
7 | {:extra-paths ["dev"]
8 | :extra-deps {nrepl/nrepl {:mvn/version "1.3.0"}
9 | cider/cider-nrepl {:mvn/version "0.49.0"}}
10 | :jvm-opts ["-Djdk.attach.allowAttachSelf"]
11 | :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}}}
12 |
--------------------------------------------------------------------------------
/.clj-kondo/imports/io.github.humbleui/humbleui/hooks/deflazy.clj_kondo:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc hooks.deflazy
2 | "A clj-kondo hook to allow linting of humbleui `loopr` macro."
3 | (:require
4 | [clj-kondo.hooks-api :as api]))
5 |
6 | (defn deflazy-hook
7 | "(deflazy paragraph ([text] [opts text]) \"paragraph\")"
8 | [form]
9 | (let [[_ name arglists file] (:children (:node form))]
10 | {:node
11 | (api/list-node
12 | (list*
13 | (api/token-node 'defn)
14 | name
15 | (for [arglist arglists]
16 | (api/list-node
17 | (list arglist)))))}))
18 |
--------------------------------------------------------------------------------
/src/file_picker/core.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp app []
10 | [ui/center
11 | [ui/label "Hello, world!"]])
12 |
13 | (defn -main [& args]
14 | (ui/start-app!
15 | (ui/window
16 | {:title "File picker"
17 | :mac-icon "resources/icon.icns"}
18 | #'app)))
19 |
20 | (comment
21 | ;; Start app
22 | (-main)
23 |
24 | ;; View docs
25 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
26 |
--------------------------------------------------------------------------------
/step-by-step/00_hello_world.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp app []
10 | [ui/center
11 | [ui/label "Hello, world!"]])
12 |
13 | (defn -main [& args]
14 | (ui/start-app!
15 | (ui/window
16 | {:title "File picker"
17 | :mac-icon "resources/icon.icns"}
18 | #'app)))
19 |
20 | (comment
21 | ;; Start app
22 | (-main)
23 |
24 | ;; View docs
25 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
26 |
--------------------------------------------------------------------------------
/.clj-kondo/imports/io.github.humbleui/humbleui/config.edn:
--------------------------------------------------------------------------------
1 | {:hooks
2 | {:analyze-call
3 | {io.github.humbleui.ui/deflazy hooks.deflazy/deflazy-hook
4 | io.github.humbleui.util/loopr hooks.loopr/loopr-hook
5 | io.github.humbleui.util/condp hooks.condplus/condplus-hook}}
6 | :lint-as
7 | {io.github.humbleui.ui/defcomp clojure.core/defn
8 | io.github.humbleui.util/deftype+ clojure.core/deftype
9 | io.github.humbleui.util/for-map clojure.core/for
10 | io.github.humbleui.util/for-vec clojure.core/for
11 | io.github.humbleui.util/memo-fn clojure.core/fn
12 | io.github.humbleui.util/thread clojure.core/future
13 | io.github.humbleui.util/when-some+ clojure.core/when-some}}
14 |
--------------------------------------------------------------------------------
/step-by-step/01_rect_padding.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp app []
10 | [ui/rect {:paint {:fill "EEE"}}
11 | [ui/padding {:padding 10}
12 | [ui/rect {:paint {:fill "FFF"}}
13 | [ui/center
14 | [ui/label "Hello, world!"]]]]])
15 |
16 | (defn -main [& args]
17 | (ui/start-app!
18 | (ui/window
19 | {:title "File picker"
20 | :mac-icon "resources/icon.icns"}
21 | #'app)))
22 |
23 | (comment
24 | ;; Start app
25 | (-main)
26 |
27 | ;; View docs
28 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
29 |
--------------------------------------------------------------------------------
/step-by-step/02_column_row.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp app []
10 | [ui/rect {:paint {:fill "EEE"}}
11 | [ui/padding {:padding 10}
12 | [ui/column
13 | ^:stretch
14 | [ui/rect {:paint {:fill "FFF"}}
15 | [ui/center
16 | [ui/label "Hello, world!"]]]
17 | [ui/row
18 | [ui/button {:style :default} "Open"]
19 | [ui/button "Cancel"]]]]])
20 |
21 | (defn -main [& args]
22 | (ui/start-app!
23 | (ui/window
24 | {:title "File picker"
25 | :mac-icon "resources/icon.icns"}
26 | #'app)))
27 |
28 | (comment
29 | ;; Start app
30 | (-main)
31 |
32 | ;; View docs
33 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
34 |
--------------------------------------------------------------------------------
/step-by-step/03_gap_align.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp app []
10 | [ui/rect {:paint {:fill "EEE"}}
11 | [ui/padding {:padding 10}
12 | [ui/column {:gap 10}
13 | ^:stretch
14 | [ui/rect {:paint {:fill "FFF"}}
15 | [ui/center
16 | [ui/label "Hello, world!"]]]
17 | [ui/align {:x :right}
18 | [ui/row {:gap 10}
19 | [ui/button {:style :default} "Open"]
20 | [ui/button "Cancel"]]]]]])
21 |
22 | (defn -main [& args]
23 | (ui/start-app!
24 | (ui/window
25 | {:title "File picker"
26 | :mac-icon "resources/icon.icns"}
27 | #'app)))
28 |
29 | (comment
30 | ;; Start app
31 | (-main)
32 |
33 | ;; View docs
34 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
35 |
--------------------------------------------------------------------------------
/.clj-kondo/imports/io.github.humbleui/humbleui/hooks/loopr.clj_kondo:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc hooks.loopr
2 | "A clj-kondo hook to allow linting of humbleui `loopr` macro."
3 | (:require
4 | [clj-kondo.hooks-api :as api]))
5 |
6 | (defn loopr-hook
7 | "`loopr` could be linted as
8 |
9 | (loop [acc1 []
10 | acc2 []]
11 | (let [iter1 1
12 | iter2 2]
13 | (if iter1
14 | body
15 | final)))"
16 | [form]
17 | (let [[_ accs iters body & [final]] (:children (:node form))]
18 | {:node
19 | (api/list-node
20 | (list
21 | (api/token-node 'loop)
22 | accs
23 | (api/list-node
24 | (list
25 | (api/token-node 'let)
26 | iters
27 | (api/list-node
28 | (list
29 | (api/token-node 'if)
30 | (first (:children iters))
31 | body
32 | final))))))}))
33 |
--------------------------------------------------------------------------------
/step-by-step/04_comp_state.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp file-list []
10 | (let [*dir (ui/signal
11 | (io/file (System/getProperty "user.home")))]
12 | (fn []
13 | [ui/center
14 | [ui/label "Hello, world!"]])))
15 |
16 | (ui/defcomp app []
17 | [ui/rect {:paint {:fill "EEE"}}
18 | [ui/padding {:padding 10}
19 | [ui/column {:gap 10}
20 | ^:stretch
21 | [ui/rect {:paint {:fill "FFF"}}
22 | [file-list]]
23 | [ui/align {:x :right}
24 | [ui/row {:gap 10}
25 | [ui/button {:style :default} "Open"]
26 | [ui/button "Cancel"]]]]]])
27 |
28 | (defn -main [& args]
29 | (ui/start-app!
30 | (ui/window
31 | {:title "File picker"
32 | :mac-icon "resources/icon.icns"}
33 | #'app)))
34 |
35 | (comment
36 | ;; Start app
37 | (-main)
38 |
39 | ;; View docs
40 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
41 |
--------------------------------------------------------------------------------
/step-by-step/05_file_list.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp file-list []
10 | (let [*dir (ui/signal
11 | (io/file (System/getProperty "user.home")))]
12 | (fn []
13 | [ui/vscroll
14 | [ui/padding {:vertical 8}
15 | [ui/column
16 | (for [file (->> (.listFiles @*dir)
17 | (sort-by (comp str/lower-case File/.getName)))]
18 | [ui/padding {:padding 8}
19 | [ui/label (.getName file)]])]]])))
20 |
21 | (ui/defcomp app []
22 | [ui/rect {:paint {:fill "EEE"}}
23 | [ui/padding {:padding 10}
24 | [ui/column {:gap 10}
25 | ^:stretch
26 | [ui/rect {:paint {:fill "FFF"}}
27 | [file-list]]
28 | [ui/align {:x :right}
29 | [ui/row {:gap 10}
30 | [ui/button {:style :default} "Open"]
31 | [ui/button "Cancel"]]]]]])
32 |
33 | (defn -main [& args]
34 | (ui/start-app!
35 | (ui/window
36 | {:title "File picker"
37 | :mac-icon "resources/icon.icns"}
38 | #'app)))
39 |
40 | (comment
41 | ;; Start app
42 | (-main)
43 |
44 | ;; View docs
45 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
46 |
--------------------------------------------------------------------------------
/.clj-kondo/imports/io.github.humbleui/humbleui/hooks/condplus.clj_kondo:
--------------------------------------------------------------------------------
1 | (ns ^:no-doc hooks.condplus
2 | "A clj-kondo hook to allow linting of humbleui `cond+` macro."
3 | (:require
4 | [clj-kondo.hooks-api :as api]))
5 |
6 | (defn condplus-hook [form]
7 | (let [[_ test expr & rest] (:children (:node form))
8 | tail (when rest
9 | (api/list-node
10 | (list*
11 | (api/token-node 'io.github.humbleui.core/cond+)
12 | rest)))]
13 | {:node
14 | (cond
15 | (and (api/keyword-node? test) (= :do (:k test)))
16 | (api/list-node
17 | (list
18 | (api/token-node 'do)
19 | expr
20 | tail))
21 |
22 | (and (api/keyword-node? test) (= :let (:k test)))
23 | (api/list-node
24 | (list
25 | (api/token-node 'let)
26 | expr
27 | tail))
28 |
29 | (and (api/keyword-node? test) (= :some (:k test)))
30 | (api/list-node
31 | (list
32 | (api/token-node 'or)
33 | expr
34 | tail))
35 |
36 | tail
37 | (api/list-node
38 | (list
39 | (api/token-node 'if)
40 | test
41 | expr
42 | tail))
43 |
44 | :else
45 | (api/list-node
46 | (list
47 | (api/token-node 'when)
48 | test
49 | expr)))}))
50 |
--------------------------------------------------------------------------------
/step-by-step/06_selectable.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp file-row [file *selected]
10 | [ui/clickable
11 | {:on-click
12 | (fn [_event]
13 | (reset! *selected file))}
14 | [ui/rect
15 | {:paint {:fill (if (= file @*selected)
16 | "BBDDFF"
17 | "FFFFFF")}}
18 | [ui/padding {:padding 8}
19 | [ui/label (.getName file)]]]])
20 |
21 | (ui/defcomp file-list []
22 | (let [*dir (ui/signal
23 | (io/file (System/getProperty "user.home")))
24 | *selected (ui/signal nil)]
25 | (fn []
26 | [ui/vscroll
27 | [ui/padding {:vertical 8}
28 | [ui/column
29 | (for [file (->> (.listFiles @*dir)
30 | (sort-by (comp str/lower-case File/.getName)))]
31 | [file-row file *selected])]]])))
32 |
33 | (ui/defcomp app []
34 | [ui/rect {:paint {:fill "EEE"}}
35 | [ui/padding {:padding 10}
36 | [ui/column {:gap 10}
37 | ^:stretch
38 | [ui/rect {:paint {:fill "FFF"}}
39 | [file-list]]
40 | [ui/align {:x :right}
41 | [ui/row {:gap 10}
42 | [ui/button {:style :default} "Open"]
43 | [ui/button "Cancel"]]]]]])
44 |
45 | (defn -main [& args]
46 | (ui/start-app!
47 | (ui/window
48 | {:title "File picker"
49 | :mac-icon "resources/icon.icns"}
50 | #'app)))
51 |
52 | (comment
53 | ;; Start app
54 | (-main)
55 |
56 | ;; View docs
57 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
58 |
--------------------------------------------------------------------------------
/step-by-step/07_hoverable.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp file-row [file *selected]
10 | [ui/clickable
11 | {:on-click
12 | (fn [_event]
13 | (reset! *selected file))}
14 | (fn [state]
15 | [ui/rect
16 | {:paint {:fill (cond
17 | (= file @*selected) "BBDDFF"
18 | (:hovered state) "E0F0FF"
19 | :else "FFFFFF")}}
20 | [ui/padding {:padding 8}
21 | [ui/label (.getName file)]]])])
22 |
23 | (ui/defcomp file-list []
24 | (let [*dir (ui/signal
25 | (io/file (System/getProperty "user.home")))
26 | *selected (ui/signal nil)]
27 | (fn []
28 | [ui/vscroll
29 | [ui/padding {:vertical 8}
30 | [ui/column
31 | (for [file (->> (.listFiles @*dir)
32 | (sort-by (comp str/lower-case File/.getName)))]
33 | [file-row file *selected])]]])))
34 |
35 | (ui/defcomp app []
36 | [ui/rect {:paint {:fill "EEE"}}
37 | [ui/padding {:padding 10}
38 | [ui/column {:gap 10}
39 | ^:stretch
40 | [ui/rect {:paint {:fill "FFF"}}
41 | [file-list]]
42 | [ui/align {:x :right}
43 | [ui/row {:gap 10}
44 | [ui/button {:style :default} "Open"]
45 | [ui/button "Cancel"]]]]]])
46 |
47 | (defn -main [& args]
48 | (ui/start-app!
49 | (ui/window
50 | {:title "File picker"
51 | :mac-icon "resources/icon.icns"}
52 | #'app)))
53 |
54 | (comment
55 | ;; Start app
56 | (-main)
57 |
58 | ;; View docs
59 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
60 |
--------------------------------------------------------------------------------
/step-by-step/08_current_dir.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (ui/defcomp file-row [file *selected]
10 | [ui/clickable
11 | {:on-click
12 | (fn [_event]
13 | (reset! *selected file))}
14 | (fn [state]
15 | [ui/rect
16 | {:paint {:fill (cond
17 | (= file @*selected) "BBDDFF"
18 | (:hovered state) "E0F0FF"
19 | :else "FFFFFF")}}
20 | [ui/padding {:padding 8}
21 | [ui/label (.getName file)]]])])
22 |
23 | (ui/defcomp file-list []
24 | (let [*dir (ui/signal
25 | (io/file (System/getProperty "user.home")))
26 | *selected (ui/signal nil)]
27 | (fn []
28 | [ui/vscroll
29 | [ui/padding {:vertical 8}
30 | [ui/column
31 | (for [file (->> (.listFiles @*dir)
32 | (sort-by (comp str/lower-case File/.getName)))]
33 | [file-row file *selected])]]])))
34 |
35 | (ui/defcomp app []
36 | [ui/rect {:paint {:fill "EEE"}}
37 | [ui/padding {:padding 10}
38 | [ui/column {:gap 10}
39 | ^:stretch
40 | [ui/rect {:paint {:fill "FFF"}}
41 | [file-list]]
42 | [ui/row {:gap 10}
43 | [ui/align {:y :center}
44 | [ui/label @*dir]]
45 | ^:stretch [ui/gap]
46 | [ui/button {:style :default} "Open"]
47 | [ui/button "Cancel"]]]]])
48 |
49 | (defn -main [& args]
50 | (ui/start-app!
51 | (ui/window
52 | {:title "File picker"
53 | :mac-icon "resources/icon.icns"}
54 | #'app)))
55 |
56 | (comment
57 | ;; Start app
58 | (-main)
59 |
60 | ;; View docs
61 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
62 |
--------------------------------------------------------------------------------
/humble-file-picker.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/step-by-step/09_go_down.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (def *dir
10 | (ui/signal
11 | (io/file (System/getProperty "user.home"))))
12 |
13 | (def *selected
14 | (ui/signal nil))
15 |
16 | (ui/defcomp file-row [file]
17 | [ui/clickable
18 | {:on-click
19 | (fn [event]
20 | (cond
21 | (and
22 | (>= (:clicks event) 2)
23 | (.isDirectory file))
24 | (do
25 | (reset! *dir file)
26 | (reset! *selected nil))
27 |
28 | :else
29 | (reset! *selected file)))}
30 | (fn [state]
31 | [ui/rect
32 | {:paint {:fill (cond
33 | (= file @*selected) "BBDDFF"
34 | (:hovered state) "E0F0FF"
35 | :else "FFFFFF")}}
36 | [ui/padding {:padding 8}
37 | [ui/label (.getName file)]]])])
38 |
39 | (ui/defcomp file-list []
40 | [ui/vscroll
41 | [ui/padding {:vertical 8}
42 | [ui/column
43 | (for [file (->> (.listFiles @*dir)
44 | (sort-by (comp str/lower-case File/.getName)))]
45 | [file-row file])]]])
46 |
47 | (ui/defcomp app []
48 | [ui/rect {:paint {:fill "EEE"}}
49 | [ui/padding {:padding 10}
50 | [ui/column {:gap 10}
51 | ^:stretch
52 | [ui/rect {:paint {:fill "FFF"}}
53 | [file-list]]
54 | [ui/row {:gap 10}
55 | [ui/align {:y :center}
56 | [ui/label @*dir]]
57 | ^:stretch [ui/gap]
58 | [ui/button {:style :default} "Open"]
59 | [ui/button "Cancel"]]]]])
60 |
61 | (defn -main [& args]
62 | (ui/start-app!
63 | (ui/window
64 | {:title "File picker"
65 | :mac-icon "resources/icon.icns"}
66 | #'app)))
67 |
68 | (comment
69 | ;; Start app
70 | (-main)
71 |
72 | ;; View docs
73 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
74 |
--------------------------------------------------------------------------------
/step-by-step/10_go_up.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (def *dir
10 | (ui/signal
11 | (io/file (System/getProperty "user.home"))))
12 |
13 | (def *selected
14 | (ui/signal nil))
15 |
16 | (ui/defcomp file-row [file]
17 | [ui/clickable
18 | {:on-click
19 | (fn [event]
20 | (cond
21 | (and
22 | (>= (:clicks event) 2)
23 | (= ".." (.getName file)))
24 | (do
25 | (swap! *dir File/.getParentFile)
26 | (reset! *selected nil))
27 |
28 | (and
29 | (>= (:clicks event) 2)
30 | (.isDirectory file))
31 | (do
32 | (reset! *dir file)
33 | (reset! *selected nil))
34 |
35 | :else
36 | (reset! *selected file)))}
37 | (fn [state]
38 | [ui/rect
39 | {:paint {:fill (cond
40 | (= file @*selected) "BBDDFF"
41 | (:hovered state) "E0F0FF"
42 | :else "FFFFFF")}}
43 | [ui/padding {:padding 8}
44 | [ui/label (.getName file)]]])])
45 |
46 | (ui/defcomp file-list []
47 | [ui/vscroll
48 | [ui/padding {:vertical 8}
49 | [ui/column
50 | (when (.getParentFile @*dir)
51 | [file-row (io/file "..")])
52 | (for [file (->> (.listFiles @*dir)
53 | (sort-by (comp str/lower-case File/.getName)))]
54 | [file-row file])]]])
55 |
56 | (ui/defcomp app []
57 | [ui/rect {:paint {:fill "EEE"}}
58 | [ui/padding {:padding 10}
59 | [ui/column {:gap 10}
60 | ^:stretch
61 | [ui/rect {:paint {:fill "FFF"}}
62 | [file-list]]
63 | [ui/row {:gap 10}
64 | [ui/align {:y :center}
65 | [ui/label @*dir]]
66 | ^:stretch [ui/gap]
67 | [ui/button {:style :default} "Open"]
68 | [ui/button "Cancel"]]]]])
69 |
70 | (defn -main [& args]
71 | (ui/start-app!
72 | (ui/window
73 | {:title "File picker"
74 | :mac-icon "resources/icon.icns"}
75 | #'app)))
76 |
77 | (comment
78 | ;; Start app
79 | (-main)
80 |
81 | ;; View docs
82 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
83 |
--------------------------------------------------------------------------------
/step-by-step/11_icons.clj:
--------------------------------------------------------------------------------
1 | (ns file-picker.core
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [io.github.humbleui.ui :as ui])
6 | (:import
7 | [java.io File]))
8 |
9 | (def *dir
10 | (ui/signal
11 | (io/file (System/getProperty "user.home"))))
12 |
13 | (def *selected
14 | (ui/signal nil))
15 |
16 | (ui/defcomp file-row [file]
17 | [ui/clickable
18 | {:on-click
19 | (fn [event]
20 | (cond
21 | (and
22 | (>= (:clicks event) 2)
23 | (= ".." (.getName file)))
24 | (do
25 | (swap! *dir File/.getParentFile)
26 | (reset! *selected nil))
27 |
28 | (and
29 | (>= (:clicks event) 2)
30 | (.isDirectory file))
31 | (do
32 | (reset! *dir file)
33 | (reset! *selected nil))
34 |
35 | :else
36 | (reset! *selected file)))}
37 | (fn [state]
38 | [ui/rect
39 | {:paint {:fill (cond
40 | (= file @*selected) "BBDDFF"
41 | (:hovered state) "E0F0FF"
42 | :else "FFFFFF")}}
43 | [ui/padding {:horizontal 8}
44 | [ui/row {:gap 4
45 | :align :center}
46 | [ui/size {:width 24 :height 24}
47 | (if (.isDirectory file)
48 | [ui/svg {:src "resources/folder.svg"}]
49 | [ui/svg {:src "resources/file.svg"}])]
50 | [ui/padding {:vertical 8}
51 | [ui/label (.getName file)]]]]])])
52 |
53 | (ui/defcomp file-list []
54 | [ui/vscroll
55 | [ui/padding {:vertical 8}
56 | [ui/column
57 | (when (.getParentFile @*dir)
58 | [file-row (io/file "..")])
59 | (for [file (->> (.listFiles @*dir)
60 | (sort-by (comp str/lower-case File/.getName)))]
61 | [file-row file])]]])
62 |
63 | (ui/defcomp app []
64 | [ui/rect {:paint {:fill "EEE"}}
65 | [ui/padding {:padding 10}
66 | [ui/column {:gap 10}
67 | ^:stretch
68 | [ui/rect {:paint {:fill "FFF"}}
69 | [file-list]]
70 | [ui/row {:gap 10}
71 | [ui/align {:y :center}
72 | [ui/label @*dir]]
73 | ^:stretch [ui/gap]
74 | [ui/button {:style :default} "Open"]
75 | [ui/button "Cancel"]]]]])
76 |
77 | (defn -main [& args]
78 | (ui/start-app!
79 | (ui/window
80 | {:title "File picker"
81 | :mac-icon "resources/icon.icns"}
82 | #'app)))
83 |
84 | (comment
85 | ;; Start app
86 | (-main)
87 |
88 | ;; View docs
89 | ((requiring-resolve 'io.github.humbleui.docs/open!)))
90 |
--------------------------------------------------------------------------------