Re: confusion (aka specifiers...)
"Stephen J. Turnbull" <[email protected]> Tue, 15 Jun 2004 12:01:04 +0900
| Newsgroups | gmane.emacs.xemacs.design |
|---|---|
| Organization | The XEmacs Project |
| Message-ID | <[email protected]> |
>>>>> "giacomo" == giacomo boffi <[email protected]> writes: giacomo> Stephen J. Turnbull writes: >> BTW, I can't get this to work at all. :-( I hope it's just >> this particular semi-hacked binary I'm on at the moment.... giacomo> the piece of code below works for me (with a bit of giacomo> flicker) Yeah, I got it working, I'm not sure what was going on. I don't see a flicker, but I suspect what you are seeing is that fact that two separate hooks are used. It's possible to use the fact that specifiers are more powerful than merely "frame-local" to do all the work in the select-frame-hook. (See below.) I propose to discuss your feature in the Lispref. I think it nicely exposes some neat features of specifiers in a simple example. I'd appreciate comments on whether it makes sense to you, and any questions it raises. Definitely let me know your preferences about attribution. Also, as you may know, I dislike the GFDL and took the author's prerogative to deliberately leave it out of the permission statement. But if you would like it added, I have no objection. Index: man/ChangeLog =================================================================== RCS file: /pack/xemacscvs/XEmacs/xemacs/man/ChangeLog,v retrieving revision 1.253 diff -u -U0 -r1.253 ChangeLog --- man/ChangeLog 14 Jun 2004 10:24:44 -0000 1.253 +++ man/ChangeLog 15 Jun 2004 02:51:51 -0000 @@ -0,0 +1,5 @@ +2004-06-15 Stephen J. Turnbull <[email protected]> + + * lispref/specifiers.texi (Specifier Instancing): Add Giacomo + Boffi's modeline hack as an example. + Index: man/lispref/specifiers.texi =================================================================== RCS file: /pack/xemacscvs/XEmacs/xemacs/man/lispref/specifiers.texi,v retrieving revision 1.13 diff -u -r1.13 specifiers.texi --- man/lispref/specifiers.texi 19 Apr 2004 08:02:38 -0000 1.13 +++ man/lispref/specifiers.texi 15 Jun 2004 02:51:58 -0000 @@ -471,6 +471,160 @@ over a device domain looks only for device locales and the @code{global} locale. +Note that specifiers are instanced on @emph{every} redisplay. (This is +the concept; of course the implementation keeps track of changes and +doesn't reinstance unchanged specifiers.) That means that changes in +specifiers controlling appearance are reflected immediately in the UI. +Also, since specifiers are instanced completely, removing a +specification can be just as effective as adding one. + +@emph{E.g.}, Giacomo Boffi wanted a modeline that indicates whether the frame +containing it is selected or not. The first proposed implementation is +natural in a world of ``local'' variables. + +(The copright notice and permission statement below apply to the code in +example format, up to the ``@code{;;; end of boffi-modeline-hack.el}'' +comment.) + +@example +;;; boffi-modeline-hack.el + +;; Copyright (c) 2004 Giacomo Boffi <giacomo.boffi@@polimi.it> +;; Copyright (c) 2004 Stephen J. Turnbull <stephen@@xemacs.org> + +;; This code may be used and redistributed under the GNU GPL, v.2 or any +;; later version as published by the FSF, or under the license used for +;; XEmacs Texinfo manuals, at your option. +@end example + +A few customizations: + +@example +;; Placate the specifier and Customize gods. + +(unless (valid-specifier-tag-p 'modeline-background) + (define-specifier-tag 'modeline-background)) + +(defgroup lisp-demos nil "Demos for Lisp programming techniques.") + +(defgroup boffi-modeline-hack nil "Giacomo Boffi's modeline hack." +:group 'lisp-demos) + +;; Boffi prefers red2 and red3 here. + +(defcustom boffi-modeline-hack-selected-background "LemonChiffon" + "Background color for modeline in selected frames." +:type 'color +:group 'boffi-modeline-hack) + +(defcustom boffi-modeline-hack-deselected-background "Wheat" + "Background color for modeline in unselected frames." +:type 'color +:group 'boffi-modeline-hack) + +@end example + +Our first try uses three functions, a setup and two hook functions. +Separate hooks are defined for entry into a frame and exit from it. +Since we're using hooks, it's a fairly natural approach: we operate on +the background on each event corresponding to an appearance change we +want to make. This doesn't depend on the specifier API, ``frame-local'' +variables would serve as well. + +@example +(defun select-modeline () + (set-face-background 'modeline boffi-modeline-hack-selected-background + (selected-frame))) + +(defun deselect-modeline () + (set-face-background 'modeline boffi-modeline-hack-deselected-background + (selected-frame))) +@end example + +Note that the initialization removes no specifications, and therefore is +not idempotent. Cruft will accumulate in the specifier if the +@samp{-setup} function is called repeatedly. This shouldn't cause a +performance problem; specifiers are quite efficient for their purpose. +But it's ugly, and wastes a small amount of space. + +@example +(defun boffi-modeline-hack-setup () + (interactive) + (set-face-background 'modeline boffi-modeline-hack-deselected-background) + ;; Add the distinguished background on pointer entry to the frame; + (add-hook 'select-frame-hook 'select-modeline) + ;; restore the ordinary background on pointer exit from the frame. + (add-hook 'deselect-frame-hook 'deselect-modeline) +@end example + +This approach causes a decided flicker on Boffi's platform, because the +two hook functions are executed in response to separate GUI events. + +The following code should be an improvement. First, the hook function. + +@example +(defun reselect-modeline () + (set-face-background 'modeline boffi-modeline-hack-deselected-background + (selected-frame) '(modeline-background) + 'remove-locale-type)) +@end example + +Only one event triggers the configuration change, which should reduce +flicker. Because this is implemented as a specifier, we can use the +specifier API to reset all frame-local specifications (the +@code{remove-locale-type} argument). This leaves the @code{global} +specification alone, but removes all existing frame-local +specifications. Then it adds the selected-frame background +specification for the newly selected frame. @emph{I.e.}, this +effectively implements a ``move specification from frame to frame'' +operation. + +Why does it give the desired result? By ensuring that only one frame +has the selected-frame modeline background. Frame-local specifications +have precedence over global ones, so when the modeline background is +instantiated in the selected frame, it matches the specification set up +for it and gets the right color. On the other hand, in any other frame, +it does not match the selected frame, so it falls through the +frame-local specifications and picks up the global specification. Again +we get the desired color for unselected frames. + +Here the @code{modeline-background} tag is simply good practice +(identifying an application's specifications allows it to avoid +interfering with other applications, and other well-behaved applications +and Customize should not munge specifications with our tag on them). +However, an alternative implementation of this functionality would be + +@example +(defun reselect-modeline-2 () + (set-face-background 'modeline boffi-modeline-hack-deselected-background + (selected-frame) '(modeline-background) + 'remove-tag-set-prepend)) +@end example + +@code{reselect-modeline} may be a preferable implementation here, if we +really want only one frame to have a local specification. The +@code{reselect-modeline-2} style would be useful if we had groups of +frames which have @emph{different} modeline backgrounds when deselected. + +Here's the initialization function, with different semantics from above. +Note that it is destructive, unless the user saves off the state of the +modeline face before invoking the function. We use the customizations +defined above. + +@example +(defun boffi-modified-modeline-hack-setup () + (interactive) + (set-face-background 'modeline boffi-modeline-hack-selected-background + 'global nil 'remove-all) + (add-hook 'select-frame-hook 'reselect-modeline) + +;;; end of boffi-modeline-hack.el +@end example + +Note the use of @code{'remove-all} to clear out stale specifications. +Thus it will be idempotent. + + @node Specifier Types @section Specifier Types -- Institute of Policy and Planning Sciences http://turnbull.sk.tsukuba.ac.jp University of Tsukuba Tennodai 1-1-1 Tsukuba 305-8573 JAPAN Ask not how you can "do" free software business; ask what your business can "do for" free software.