cvs import
[celtk.git] / composites.lisp
blob883f4b35b92e19f924fbbb157d2008087b5f1aea
1 ;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cells; -*-
2 #|
4 Celtk -- Cells, Tcl, and Tk
6 Copyright (C) 2006 by Kenneth Tilton
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the Lisp Lesser GNU Public License
10 (http://opensource.franz.com/preamble.html), known as the LLGPL.
12 This library is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the Lisp Lesser GNU Public License for more details.
19 (in-package :Celtk)
21 (eval-now!
22 (export '(title$ active .time decoration)))
24 (export! application
25 keyboard-modifiers
26 iconify
27 deiconify
28 full-screen-no-deco-window
29 screen-width
30 screen-height)
32 ;;; --- decoration -------------------------------------------
34 (defmd decoration-mixin ()
35 (decoration (c-in nil)))
37 ;;; --- toplevel ---------------------------------------------
39 (deftk toplevel (widget decoration-mixin)
41 (:tk-spec toplevel
42 -borderwidth -cursor -highlightbackground -highlightcolor
43 -highlightthickness -padx -pady -relief
44 -takefocus -background -tk-class -colormap
45 -container -height -menu -screen
46 -use -visual -width)
47 (:default-initargs
48 :id (gentemp "TOP")))
50 ;; --- panedwindow -----------------------------------------
52 (deftk panedwindow (widget decoration-mixin)
54 (:tk-spec panedwindow
55 -background -borderwidth -cursor -height
56 -orient -relief -width
57 -handlepad
58 -handlesize
59 -opaqueresize
60 -sashcursor
61 -sashpad
62 -sashrelief
63 -sashwidth
64 -showhandle)
65 (:default-initargs
66 :id (gentemp "PW")
67 :packing nil))
69 (defmethod make-tk-instance ((self panedwindow))
70 (tk-format `(:make-tk ,self) "panedwindow ~a -orient ~(~a~)"
71 (^path) (or (orient self) "vertical"))
72 (tk-format `(:pack ,self) "pack ~a -expand yes -fill both" (^path)))
74 (defmethod parent-path ((self panedwindow)) (^path))
76 (defobserver .kids ((self panedwindow))
77 (loop for k in (^kids)
78 do (trc "panedwindow adds" k (type-of k) (md-name k) (path k))
79 (tk-format `(:post-make-tk ,self) "~a add ~a" (^path) (path k))))
81 ; --------------------------------------------------------
83 (defmodel composite-widget (widget)
84 ((kids-packing :initarg :kids-packing :accessor kids-packing :initform nil)))
86 (defvar *app*)
88 (defmodel application (family)
89 ((app-time :initform (c-in (now))
90 :initarg :app-time
91 :accessor app-time)))
93 (define-symbol-macro .time (app-time *app*))
95 (defmethod path ((self application)) nil)
97 (defvar *app-idle-tasks*)
98 (defun app-idle-tasks-clear ()
99 (setf *app-idle-tasks* nil))
100 (defun app-idle-task-new (task-fn)
101 (push task-fn *app-idle-tasks*)
102 *app-idle-tasks*)
104 (defun app-idle-task-destroy (task-fn)
105 (setf *app-idle-tasks*
106 (delete task-fn *app-idle-tasks*)))
108 #+crazier
109 (defun app-idle-task-destroy (task-cell)
110 (setf *app-idle-tasks*
111 (if (eq task-cell *app-idle-tasks*)
112 (cdr *app-idle-tasks*)
113 (mapl (lambda (tasks)
114 (when (eq task-cell (cdr tasks))
115 (rplacd tasks (cdr task-cell))))))))
118 (defun app-idle (self)
119 (loop for w in (^kids)
120 do (when (not (eq :arrow (cursor w)))
121 (setf (cursor w) :arrow)))
122 (setf (^app-time) (now))
123 (loop for task in *app-idle-tasks*
124 do (funcall task self task)))
126 (defmd window (toplevel composite-widget decoration-mixin)
127 (title$ (c? (string-capitalize (class-name (class-of self)))))
128 (dictionary (make-hash-table :test 'equalp))
129 (tkwins (make-hash-table))
130 (xwins (make-hash-table))
131 (cursor :arrow :cell nil)
132 (keyboard-modifiers (c-in nil))
133 (callbacks (make-hash-table :test #'eq))
134 (edit-style (c-in nil))
135 (tk-scaling (c? 1.3 #+tki (read-from-string (tk-eval "tk scaling"))))
136 tkfonts-to-load
137 tkfont-sizes-to-load
138 (tkfont-info (tkfont-info-loader))
139 start-up-fn
140 close-fn
141 initial-focus
142 (focus-state (c-in nil) :documentation "This is about the window having the focus on the desktop, not the key focus.
143 Actually holds last event code, :focusin or :focusout")
144 on-key-down
145 on-key-up
146 :width (c?n 800)
147 :height (c?n 600))
149 (defmethod (setf cursor) :after (new-value (self window))
150 (when new-value
151 (tk-format-now ". configure -cursor ~a" (string-downcase (symbol-name new-value)))))
153 (export! .control-key-p .alt-key-p .shift-key-p focus-state ^focus-state)
154 (define-symbol-macro .control-key-p (find :control (keyboard-modifiers .tkw)))
155 (define-symbol-macro .alt-key-p (find :alt (keyboard-modifiers .tkw)))
156 (define-symbol-macro .shift-key-p (find :shift (keyboard-modifiers .tkw)))
158 (defmethod make-tk-instance ((self window))
159 (setf (gethash (^path) (dictionary .tkw)) self))
161 (defun screen-width ()
162 (let ((*tkw* *tkw*))
163 (tk-format-now "winfo screenwidth .")))
165 (defun screen-height ()
166 (let ((*tkw* *tkw*))
167 (tk-format-now "winfo screenheight .")))
169 (defmodel full-screen-no-deco-window (window)
172 (defmethod initialize-instance :before ((self full-screen-no-deco-window)
173 &key &allow-other-keys)
174 (tk-format '(:pre-make-tk self)
175 "wm geometry . [winfo screenwidth .]x[winfo screenheight .]+0+0")
176 (tk-format '(:pre-make-tk self) "update idletasks")
177 #-macosx (tk-format '(:pre-make-tk self) "wm attributes . -topmost yes")
178 (tk-format '(:pre-make-tk self) "wm overrideredirect . yes")
181 (defmethod do-on-key-down :before (self &rest args &aux (keysym (car args)))
182 (trc nil "ctk::do-on-key-down window" keysym (keyboard-modifiers .tkw))
183 (bwhen (mod (keysym-to-modifier keysym))
184 (eko (nil "modifiers after adding" mod)
185 (pushnew mod (keyboard-modifiers .tkw)))))
187 (defmethod do-on-key-up :before (self &rest args &aux (keysym (car args)))
188 (trc nil "ctk::do-on-key-up before" keysym (keyboard-modifiers .tkw))
189 (bwhen (mod (keysym-to-modifier keysym))
190 (eko (nil "modifiers after removing" mod)
191 (setf (keyboard-modifiers .tkw)
192 (delete mod (keyboard-modifiers .tkw))))))
194 ;;; Helper function that actually executes decoration change
195 (defun %%do-decoration (widget decoration)
196 (let ((path (path widget)))
197 (case decoration
198 (:none
199 (progn
200 (tk-format '(:pre-make-tk decoration)
201 "wm withdraw ~a" path)
202 (tk-format '(:pre-make-tk decoration)
203 "wm overrideredirect ~a 1" path)
204 (tk-format '(:pre-make-tk decoration)
205 "wm deiconify ~a" path)
206 (tk-format '(:pre-make-tk decoration)
207 "update idletasks" path)
209 (:normal
210 (progn
211 (tk-format '(:pre-make-tk decoration)
212 "wm withdraw ~a" path)
213 (tk-format '(:pre-make-tk decoration)
214 "wm overrideredirect ~a 0" path)
215 (tk-format '(:pre-make-tk decoration)
216 "wm deiconify ~a" path)
217 (tk-format '(:pre-make-tk decoration)
218 "update idletasks" path))))))
220 ;;; Decoration observer for all widgets that inherit from decoration-mixin
221 ;;; On Mac OS X this is a one-way operation. When created without decorations
222 ;;; then it is not possible to restore the decorations and vice versa. So on
223 ;;; OS X the window decoration will stay as you created the window with.
225 (defobserver decoration ((self decoration-mixin)) ;; == wm overrideredirect 0|1
226 (assert (or (eq new-value nil) ;; Does not change decoration
227 (eq new-value :normal) ;; "normal"
228 (eq new-value :none))) ;; No title bar, no nothing ...
229 (if (not (eq new-value old-value))
230 (%%do-decoration self new-value)))
232 (defobserver initial-focus ()
233 (when new-value
234 (tk-format '(:fini new-value) "focus ~a" (path new-value))))
236 (defun tkfont-info-loader ()
237 (c? (eko (nil "tkfinfo")
238 (loop with scaling = (^tk-scaling)
239 for (tkfont fname) in (^tkfonts-to-load)
240 collect (cons tkfont
241 (apply 'vector
242 (loop for fsize in (^tkfont-sizes-to-load)
243 for id = (format nil "~(~a-~2,'0d~)" tkfont fsize)
244 for tkf = (tk-eval "font create ~a -family {~a} -size ~a"
245 id fname fsize)
246 for (nil ascent nil descent nil linespace nil fixed) = (tk-eval-list "font metrics ~a" tkf)
247 collect
248 (progn (trc nil "tkfontloaded" id fname fsize tkfont tkf)
249 (make-tkfinfo :ascent (round (parse-integer ascent :junk-allowed t) scaling)
250 :id id
251 :family fname
252 :size fsize
253 :descent (round (parse-integer descent :junk-allowed t) scaling)
254 :linespace (round (parse-integer linespace :junk-allowed t) scaling)
255 :fixed (plusp (parse-integer fixed :junk-allowed t))
256 :em (round (parse-integer
257 (tk-eval "font measure ~(~a~) \"m\"" tkfont) :junk-allowed t)
258 scaling))))))))))
260 (defobserver title$ ((self window))
261 (tk-format '(:configure "title") "wm title . ~s" (or new-value "Untitled")))
263 (defmethod path ((self window)) ".")
264 (defmethod parent-path ((self window)) "")
266 (defmethod iconify ((self window))
267 (%%do-decoration self :normal)
268 (tk-format `(:fini) "wm iconify ~a" (^path)))
270 (defmethod deiconify ((self window))
271 (%%do-decoration self (decoration self))
272 (tk-format `(:fini) "wm deiconify ~a" (^path)))