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.