Blob


1 (defpackage #:phos/ui
2 (:documentation "User Interface for phos")
3 (:use #:cl #:nodgui))
5 (in-package #:phos/ui)
7 (defparameter *title-1-font* "serif 22"
8 "Font for the level 1 title.")
10 (defparameter *title-2-font* "serif 19"
11 "Font for the level 2 title.")
13 (defparameter *title-3-font* "serif 16"
14 "Font for the level 3 title.")
16 (defparameter *verbatim-font* "monospace 12"
17 "Font for the verbatim element.")
19 (defparameter *item-font* "serif 12"
20 "Font for the item.")
22 (defparameter *link-font* "serif 12"
23 "Font for the links.")
25 (defparameter *blockquote-font* "serif 12 italic"
26 "Font for the quotations.")
28 (defparameter *paragraph-font* "serif 12"
29 "Font for the normal text")
31 (defparameter *url-bar* nil)
32 (defparameter *window-content* nil)
34 (defparameter *current-url* nil)
36 (defun join-paths (url path query)
37 (setf (quri:uri-query url) query)
38 (if (uiop:string-prefix-p "/" path)
39 (setf (quri:uri-path url) path)
40 (let ((p (quri:uri-path url)))
41 (setf (quri:uri-path url)
42 (format nil "~a~a"
43 (if (uiop:string-suffix-p "/" p)
44 p
45 (directory-namestring p))
46 path))))
47 url)
49 (defun navigate-to (uri)
50 (let ((hostname (quri:uri-host uri))
51 (path (quri:uri-path uri))
52 (query (quri:uri-query uri)))
53 (if hostname
54 (do-render uri)
55 (do-render (join-paths (quri:copy-uri *current-url*)
56 path
57 query)))))
59 (defgeneric render (obj frame)
60 (:documentation "Render OBJ in the nodgui FRAME"))
62 (defmethod render ((l list) f)
63 (dolist (el l)
64 (render el f)))
66 (defmethod render ((title gemtext:title) f)
67 (with-slots ((text phos/gemtext:text)
68 (level phos/gemtext:level))
69 title
70 (let ((w (make-instance 'label
71 :master f
72 :font (case level
73 (1 *title-1-font*)
74 (2 *title-2-font*)
75 (3 *title-3-font*))
76 :text (format nil "~v{~A~:*~} ~a" level '("#") text))))
77 (pack w :side :top :fill :both :expand t))))
79 (defmethod render ((link gemtext:link) f)
80 (with-slots ((text phos/gemtext:text)
81 (url phos/gemtext:url))
82 link
83 (let ((w (make-instance 'button
84 :master f
85 ;; :font *link-font*
86 :text (format nil "~a" (or text url))
87 :command (lambda ()
88 (navigate-to url)))))
89 (pack w :side :top :fill :both :expand t))))
91 (defmethod render ((item gemtext:item) f)
92 (with-slots ((text phos/gemtext:text)) item
93 (let ((w (make-instance 'label
94 :master f
95 :font *item-font*
96 :text (format nil "* ~a" text))))
97 (pack w :side :top :fill :both :expand t))))
99 (defmethod render ((q gemtext:blockquote) f)
100 (with-slots ((text gemtext:text)) q
101 (let ((w (make-instance 'message
102 :master f
103 :font *blockquote-font*
104 :justify :left
105 :text text
106 :width 600)))
107 (pack w :side :top :fill :both :expand t))))
109 (defmethod render ((par gemtext:paragraph) f)
110 (with-slots ((text phos/gemtext:text)) par
111 (let ((w (make-instance 'message
112 :master f
113 :font *paragraph-font*
114 :justify "left"
115 :text text
116 :width 600)))
117 ;; (setf (text w) text)
118 ;; (configure w :state "disabled")
119 (pack w :side :top :expand t :anchor "w"))))
121 (defmethod render ((v gemtext:verbatim) f)
122 (with-slots ((text phos/gemtext:text)
123 (alt phos/gemtext:alt))
125 (let ((w (make-instance 'label
126 :master f
127 :font *verbatim-font*
128 :text text)))
129 (pack w :side :top :fill :both :expand t)
130 (when alt
131 (pack (make-instance 'label
132 :master f
133 :text alt)
134 :side :top
135 :fill :both
136 :expand t)))))
138 (defun clear-window ()
139 (pack-forget-all *window-content*))
141 (defun do-render (url)
142 (let* ((uri (quri:uri url)))
143 (setf *current-url* uri)
144 (setf (text *url-bar*) url)
145 (clear-window)
146 (render (gemtext:parse-string "# loading...")
147 *window-content*)
148 (multiple-value-bind (status body) (gemini:request url)
149 (declare (ignore status))
150 (clear-window)
151 (render (gemtext:parse-string body)
152 *window-content*))))
154 (defun navigate-button-cb ()
155 (navigate-to (quri:uri (string-trim '(#\newline #\space) (text *url-bar*)))))
157 (defun main (url)
158 (with-nodgui (:title "phos")
159 (set-geometry *tk* 800 600 0 0)
160 (let* ((nav (make-instance 'frame))
161 (back-btn (make-instance 'button :text "←" :master nav))
162 (forw-btn (make-instance 'button :text "→" :master nav))
163 (go-btn (make-instance 'button :text "GO!" :master nav :command #'navigate-button-cb))
164 (url-bar (make-instance 'text :height 1 :master nav))
165 (sf (make-instance 'scrolled-frame :padding 10 :takefocus nil)))
166 (setf *url-bar* url-bar)
167 (setf *window-content* (interior sf))
168 (setf (text url-bar) "about:phos")
169 (pack nav :fill :both)
170 (pack back-btn :side :left)
171 (pack forw-btn :side :left)
172 (pack url-bar :side :left :expand t :fill :both)
173 (pack go-btn :side :left)
174 (pack sf :side :top :fill :both :expand t)
175 (do-render url))))
177 ;; (nodgui.demo:demo)
179 ;; (main "gemini://gemini.omarpolo.com/")