/ gui-lib / framework / preferences.rkt
preferences.rkt
  1  #lang at-exp racket/base
  2  #|
  3  
  4  There are three attributes for each preference:
  5  
  6    - default set, or not
  7    - marshalling function set, or not
  8    - initialization still okay, or not
  9  
 10  the state transitions / contracts are:
 11  
 12    get(true, _, _) -> (true, _, false)
 13    get(false, _, _) -> error default not yet set
 14  
 15    set is just like get.
 16  
 17    set-default(false, _, true) -> set-default(true, _, true)
 18    set-default(true, _, _) -> error default already set
 19    set-default(_, _, false) -> initialization not okay anymore  /* cannot happen, I think */
 20  
 21    set-un/marshall(true, false, true) -> (true, true, true)
 22    .. otherwise error
 23  
 24    for all syms:
 25     prefs-snapshot(_, _, _) -> (_, _, false)
 26  
 27  |#
 28  
 29  (require scribble/srcdoc
 30           racket/contract/base racket/file)
 31  (require/doc racket/base
 32               scribble/manual
 33               scribble/example
 34               (for-label racket/serialize))
 35  
 36  (define-struct (exn:unknown-preference exn) ())
 37  
 38  ;; these two names are for consistency
 39  (define exn:make-unknown-preference make-exn:unknown-preference)
 40  (define exn:struct:unknown-preference struct:exn:unknown-preference)
 41  
 42  (define preferences:low-level-put-preferences (make-parameter put-preferences))
 43  (define preferences:low-level-get-preference  (make-parameter get-preference))
 44  
 45  (define (add-pref-prefix p) (string->symbol (format "plt:framework-pref:~a" p)))
 46  
 47  ;; preferences : hash-table[sym -o> any]
 48  ;;   the current values of the preferences
 49  ;; marshall-unmarshall : sym -o> un/marshall
 50  ;; callbacks : sym -o> (listof (sym TST -> boolean))
 51  ;; defaults : hash-table[sym -o> default]
 52  (struct preferences:layer (preferences marshall-unmarshall callbacks defaults prev))
 53  
 54  (define (preferences:new-layer prev)
 55    (preferences:layer (make-hasheq) (make-hasheq) (make-hasheq) (make-hasheq) prev))
 56  (define preferences:current-layer (make-parameter (preferences:new-layer #f)))
 57  
 58  (define (find-layer pref)
 59    (let loop ([pref-state (preferences:current-layer)])
 60      (and pref-state
 61           (cond
 62             [(hash-has-key? (preferences:layer-defaults pref-state) pref)
 63              pref-state]
 64             [(hash-has-key? (preferences:layer-callbacks pref-state) pref)
 65              pref-state]
 66             [else
 67              (loop (preferences:layer-prev pref-state))]))))
 68  
 69  (define (preferences:default-set? pref)
 70    (define layer (find-layer pref))
 71    (and layer
 72         (hash-has-key? (preferences:layer-defaults layer) pref)))
 73  
 74  ;; type un/marshall = (make-un/marshall (any -> prinable) (printable -> any))
 75  (define-struct un/marshall (marshall unmarshall))
 76  
 77  ;; type pref = (make-pref any)
 78  (define-struct pref (value))
 79  
 80  ;; type default  = (make-default any (-> any bool) (listof symbol) (listof (-> any any)))
 81  (define-struct default (value checker aliases rewrite-aliases))
 82  
 83  ;; pref-callback : (make-pref-callback (union (weak-box (sym tst -> void)) (sym tst -> void)))
 84  ;; this is used as a wrapped to deal with the problem that different procedures might be eq?.
 85  (define-struct pref-callback (cb) #:transparent)
 86  
 87  ;; used to detect missing hash entries
 88  (define none (gensym 'none))
 89  
 90  ;; get : symbol -> any
 91  ;; return the current value of the preference `p'
 92  ;; exported
 93  (define (preferences:get p)
 94    (define pref-state (find-layer p))
 95    (when (or (not pref-state)
 96              (not (hash-has-key? (preferences:layer-defaults pref-state) p)))
 97      (raise-unknown-preference-error
 98       'preferences:get
 99       "tried to get a preference but preferences:set-default has not been called for ~e"
100       p))
101    (define preferences (preferences:layer-preferences pref-state))
102    (define v (hash-ref preferences p none))
103    (cond
104      ;; first time reading this, check the file & unmarshall value, if
105      ;; it's not there, use the default
106      [(eq? v none)
107       (define defaults (preferences:layer-defaults pref-state))
108       ;; try to read the preference from the preferences file
109       (define marshalled-v (read-pref-from-file (hash-ref defaults p) p))
110       (define default-info (hash-ref defaults p))
111       (define the-default-value (default-value default-info))
112       (define v (if (eq? marshalled-v none)
113                     ;; no value read, take the default value
114                     the-default-value
115                     ;; found a saved value, unmarshall it
116                     (unmarshall-pref pref-state p marshalled-v
117                                      (default-checker default-info)
118                                      the-default-value)))
119       ;; set the value in the preferences table for easier reference
120       ;; and so we know it has been read from the disk
121       ;; (and thus setting the marshaller after this is no good)
122       (hash-set! preferences p v)
123       v]
124      ;; oth. it is found, so we can just return it
125      [else v]))
126  
127  ;; read-pref-from-file : symbol -> (or/c any none)
128  ;; reads the preference saved in the low-level preferences
129  ;; file, first checking 'p' and then checking the aliases (in order)
130  (define (read-pref-from-file defaults p)
131    (let loop ([syms (cons p (default-aliases defaults))]
132               [rewriters (cons values (default-rewrite-aliases defaults))])
133      (cond
134        [(null? syms) none]
135        [else
136         (let/ec k
137           ((car rewriters)
138            ((preferences:low-level-get-preference)
139             (add-pref-prefix (car syms))
140             (lambda () (k (loop (cdr syms) (cdr rewriters)))))))])))
141  
142  ;; set : symbol any -> void
143  ;; updates the preference
144  ;; exported
145  (define (preferences:set p value) (multi-set (list p) (list value)))
146  
147  ;; set : symbol any -> void
148  ;; updates the preference
149  ;; exported
150  (define (multi-set ps values)
151    (dynamic-wind
152     (λ ()
153       (call-pref-save-callbacks #t))
154     (λ ()
155       (for ([p (in-list ps)]
156             [value (in-list values)])
157         (define pref-state (find-layer p))
158         (cond
159           [pref-state
160            (define default (hash-ref (preferences:layer-defaults pref-state) p))
161            (define checker? (default-checker default))
162            (unless (checker? value)
163              (error 'preferences:set
164                     (string-append
165                      "new value doesn't satisfy preferences:set-default predicate\n"
166                      "  pref symbol: ~e\n"
167                      "  given: ~e\n"
168                      "  predicate: ~e")
169                     p value checker?))
170            (check-callbacks pref-state p value)
171            (hash-set! (preferences:layer-preferences pref-state) p value)]
172           [else
173            (raise-unknown-preference-error
174             'preferences:set
175             (string-append
176              "cannot set preference before setting default"
177              "  pref symbol: ~e\n"
178              "  given: ~e")
179             p
180             value)]))
181       ((preferences:low-level-put-preferences)
182        (map add-pref-prefix ps)
183        (for/list ([p (in-list ps)]
184                   [value (in-list values)])
185          (marshall-pref p value)))
186       (void))
187     (λ ()
188       (call-pref-save-callbacks #f))))
189  
190  (define pref-save-callbacks '())
191  
192  (define (preferences:get/set sym)
193    (case-lambda
194      [() (preferences:get sym)]
195      [(v) (preferences:set sym v)]))
196  
197  (define (preferences:register-save-callback f)
198    (define key (gensym))
199    (set! pref-save-callbacks (cons (list key f) pref-save-callbacks))
200    key)
201  
202  (define (preferences:unregister-save-callback k)
203    (set! pref-save-callbacks
204          (let loop ([callbacks pref-save-callbacks])
205            (cond
206              [(null? callbacks) '()]
207              [else
208               (let ([cb (car callbacks)])
209                 (if (eq? (list-ref cb 0) k)
210                     (cdr callbacks)
211                     (cons cb (loop (cdr callbacks)))))]))))
212  
213  (define (call-pref-save-callbacks b)
214    (for ([cb (in-list pref-save-callbacks)])
215      ((list-ref cb 1) b)))
216  
217  (define (raise-unknown-preference-error sym fmt . args)
218    (raise (exn:make-unknown-preference
219            (string-append (format "~a: " sym) (apply format fmt args))
220            (current-continuation-marks))))
221  
222  ;; add-callback : sym (-> void) -> void
223  (define (preferences:add-callback p callback [weak? #f])
224    (define pref-state (or (find-layer p) (preferences:current-layer)))
225    (define callbacks (preferences:layer-callbacks pref-state))
226    (define new-cb
227      (make-pref-callback (if weak?
228                              (impersonator-ephemeron callback)
229                              callback)))
230    (hash-set! callbacks
231               p
232               (append
233                (hash-ref callbacks p '())
234                (list new-cb)))
235    (λ ()
236      (hash-set!
237       callbacks
238       p
239       (let loop ([callbacks (hash-ref callbacks p '())])
240         (cond
241           [(null? callbacks) '()]
242           [else
243            (let ([callback (car callbacks)])
244              (cond
245                [(eq? callback new-cb)
246                 (loop (cdr callbacks))]
247                [else
248                 (cons (car callbacks) (loop (cdr callbacks)))]))])))))
249  
250  ;; check-callbacks : pref-state sym val -> void
251  (define (check-callbacks pref-state p value)
252    (define callbacks (preferences:layer-callbacks pref-state))
253    (define new-callbacks
254      (let loop ([callbacks (hash-ref callbacks p '())])
255        (cond
256          [(null? callbacks) null]
257          [else
258           (define callback (car callbacks))
259           (define cb (pref-callback-cb callback))
260           (cond
261             [(ephemeron? cb)
262              (define v (ephemeron-value cb))
263              (cond
264                [v
265                 (v p value)
266                 (cons callback (loop (cdr callbacks)))]
267                [else
268                 (loop (cdr callbacks))])]
269             [else
270              (cb p value)
271              (cons callback (loop (cdr callbacks)))])])))
272    (if (null? new-callbacks)
273        (hash-remove! callbacks p)
274        (hash-set! callbacks p new-callbacks)))
275  
276  (define (preferences:set-un/marshall p marshall unmarshall)
277    (define pref-state (find-layer p))
278    (cond
279      [pref-state
280       (define marshall-unmarshall (preferences:layer-marshall-unmarshall pref-state))
281       (define pref-un/marshall-set? (hash-ref marshall-unmarshall p #f))
282       (define pref-can-init? (not (hash-has-key? (preferences:layer-preferences pref-state) p)))
283       (cond
284         [(and (not pref-un/marshall-set?) pref-can-init?)
285          (hash-set! marshall-unmarshall p (make-un/marshall marshall unmarshall))]
286         [pref-un/marshall-set?
287          (error 'preferences:set-un/marshall
288                 "already set un/marshall for ~e"
289                 p)]
290         [(not pref-can-init?)
291          (error 'preferences:set-un/marshall "the preference ~e cannot be configured any more" p)])]
292      [else
293       (error 'preferences:set-un/marshall
294              "must call preferences:set-default for ~s before calling set-un/marshall for ~s"
295              p p)]))
296  
297  ;; set-default : (sym TST (TST -> boolean) -> void
298  (define (preferences:set-default p default-value checker
299                                   #:aliases [aliases '()]
300                                   #:rewrite-aliases [rewrite-aliases (map (λ (x) values) aliases)])
301    (define pref-state (or (find-layer p) (preferences:current-layer)))
302    (define defaults (preferences:layer-defaults pref-state))
303    (when (hash-has-key? defaults p)
304      (error 'preferences:set-default
305             (string-append
306              "preferences default already set\n"
307              "  pref symbol: ~e\n"
308              "  default: ~e\n"
309              "  checker: ~e")
310             p default-value checker))
311    (unless (checker default-value)
312      (error 'preferences:set-default
313             (string-append
314              "checker doesn't match default\n"
315              "  pref symbol: ~e\n"
316              "  default: ~e\n"
317              "  checker: ~e")
318             p default-value checker))
319    (unless (= (length aliases) (length rewrite-aliases))
320      (error 'preferences:set-default
321             (string-append
322              "expected equal length lists for the #:aliases"
323              " and #:rewrite-aliases arguments, got ~e and ~e")
324             aliases rewrite-aliases))
325    (hash-set! defaults p (make-default default-value checker aliases rewrite-aliases)))
326  
327  ;; marshall-pref : symbol any -> (list symbol printable)
328  (define (marshall-pref p value)
329    (define pref-state (find-layer p))
330    (let/ec k
331      (define marshaller
332        (un/marshall-marshall
333         (hash-ref (preferences:layer-marshall-unmarshall pref-state)
334                   p
335                   (λ () (k value)))))
336      (marshaller value)))
337  
338  ;; unmarshall-pref : pref-state symbol marshalled (any -> bool) any -> any
339  ;; unmarshalls a preference read from the disk
340  (define (unmarshall-pref pref-state p data the-checker the-default-value)
341    (define marshall-unmarshall (preferences:layer-marshall-unmarshall pref-state))
342    (define un/marshall (hash-ref marshall-unmarshall p #f))
343    (define result
344      (if un/marshall
345          ((un/marshall-unmarshall un/marshall) data)
346          data))
347    (if (the-checker result)
348        result
349        the-default-value))
350  
351  ;; copy-pref-value : sym any -> any
352  ;; uses the marshalling code to copy a preference. If there
353  ;; is not marshaller set, then no copying happens.
354  (define (copy-pref-value p value)
355    (let/ec k
356      (define pref-state (find-layer p))
357      (define marshall-unmarshall (preferences:layer-marshall-unmarshall pref-state))
358      (define un/marshaller (hash-ref marshall-unmarshall p (λ () (k value))))
359      (define default (hash-ref (preferences:layer-defaults pref-state) p))
360      (define marsh (un/marshall-marshall un/marshaller))
361      (define unmarsh (un/marshall-unmarshall un/marshaller))
362      (define marshalled (marsh value))
363      (define copy (unmarsh marshalled))
364      (if ((default-checker default) copy)
365          copy
366          value)))
367  
368  (define (preferences:restore-defaults)
369    (let loop ([prefs-state (preferences:current-layer)])
370      (when prefs-state
371        (for ([(p def) (in-hash (preferences:layer-defaults prefs-state))])
372          (preferences:set p (default-value def)))
373        (loop (preferences:layer-prev prefs-state)))))
374  
375  (define-struct preferences:snapshot (x))
376  (define (preferences:get-prefs-snapshot)
377    (make-preferences:snapshot
378     (let loop ([prefs-state (preferences:current-layer)]
379                [sofar '()])
380       (cond
381         [prefs-state
382          (loop (preferences:layer-prev prefs-state)
383                (for/fold ([sofar sofar])
384                          ([(k def) (in-hash (preferences:layer-defaults prefs-state))])
385                  (cons (cons k (copy-pref-value k (preferences:get k)))
386                        sofar)))]
387         [else sofar]))))
388  
389  (define (preferences:restore-prefs-snapshot snapshot)
390    (multi-set (map car (preferences:snapshot-x snapshot))
391               (map cdr (preferences:snapshot-x snapshot)))
392    (void))
393  
394  (begin-for-doc
395    (define pref-layer-eval (make-base-eval))
396    (pref-layer-eval
397     '(begin
398        (require framework/preferences)
399        (let ([the-prefs-table (make-hash)])
400          (preferences:low-level-put-preferences
401           (λ (syms vals)
402             (for ([sym (in-list syms)]
403                   [val (in-list vals)])
404               (hash-set! the-prefs-table sym val))))
405          (preferences:low-level-get-preference
406           (λ (sym [fail void])
407             (hash-ref the-prefs-table sym fail)))))))
408  
409  (provide/doc
410   (proc-doc/names
411    preferences:get
412    (symbol? . -> . any/c)
413    (sym)
414    @{Returns the value for the preference @racket[sym].
415  
416   Raises an exception matching
417   @racket[exn:unknown-preference?] if the preference's default
418   has not been set.
419  
420   Use @racket[preference:set-default] to set the default value of the preference
421   before calling this function.})
422  
423   (proc-doc/names
424    preferences:set
425    (symbol? any/c . -> . void?)
426    (sym val)
427    @{Sets the preference
428   @racket[sym] to @racket[val]. It should be called when the
429   user requests a change to a preference;
430   @racket[preferences:set] immediately writes the preference value to disk.
431  
432   It raises an exception matching
433   @racket[exn:unknown-preference?]
434   if the preference's default has not been set.
435  
436   Use @racket[preference:set-default] to set the default value of the preference
437   before calling this function.})
438  
439   (proc-doc/names
440    preferences:get/set
441    (-> symbol? (case-> (-> any/c) (-> any/c void?)))
442    (pref)
443    @{Returns a procedure that when applied to zero arguments retrieves the
444      current value of the preference named @racket[pref] and when
445      applied to one argument updates the preference named @racket[pref].
446  
447      @history[#:added "1.18"]{}})
448  
449   (proc-doc/names
450    preferences:add-callback
451    (->* (symbol? (-> symbol? any/c any))
452         (boolean?)
453         (-> void?))
454    ((p f)
455     ((weak? #f)))
456    @{This function adds a callback which is called with a symbol naming a
457      preference and its value, when the preference changes.
458      @racket[preferences:add-callback] returns a thunk, which when
459      invoked, removes the callback from this preference.
460  
461      If @racket[weak?] is true, the preferences system will only hold on to
462      the callback
463      @tech[#:key "weak references"
464            #:doc '(lib "scribblings/reference/reference.scrbl")]{weakly}.
465  
466      The callbacks will be called in the order in which they were added.
467  
468      If you are adding a callback for a preference that requires
469      marshalling and unmarshalling, you must set the marshalling and
470      unmarshalling functions by calling
471      @racket[preferences:set-un/marshall] before adding a callback.
472  
473      The result thunk removes the callback from the same @tech{preferences layer}
474      that @racket[p] was in when @racket[preferences:add-callback] was
475      originally called.
476  
477      This function raises an exception matching
478      @racket[exn:unknown-preference?]
479      if the preference default has not been set via
480      @racket[preferences:set-default].})
481   (proc-doc/names
482    preferences:set-default
483    (->* (symbol? any/c (any/c . -> . any))
484         (#:aliases (listof symbol?)
485          #:rewrite-aliases (listof (-> any/c any)))
486        void?)
487    ((symbol value test)
488     ((aliases '()) (rewrite-aliases (map (lambda (x) values) aliases))))
489    @{This function must be called every time your application starts up, before
490      any call to @racket[preferences:get] or @racket[preferences:set]
491      (for any given preference).
492  
493      If you use @racket[preferences:set-un/marshall],
494      you must call this function before calling it.
495  
496      This sets the default value of the preference @racket[symbol] to
497      @racket[value]. If the user has chosen a different setting,
498      (reflected via a call to @racket[preferences:set], possibly
499      in a different run of your program),
500      the user's setting will take precedence over the default value.
501  
502      The @racket[test] argument is used as a safeguard. That function is
503      called to determine if a preference read in from a file is a valid
504      preference. If @racket[test] returns @racket[#t], then the preference is
505      treated as valid. If @racket[test] returns @racket[#f] then the default is
506      used.
507  
508      The @racket[aliases] and @racket[rewrite-aliases] arguments aids
509      in renaming preferences. If @racket[aliases] is present, it is
510      expected to be a list of symbols that correspond to old versions
511      of the preferences. It defaults to @racket['()]. If @racket[rewrite-aliases]
512      is present, it is used to adjust the old values of the preferences
513      when they are present in the saved file.
514  
515      @history[#:changed "1.23" @list{Allow @racket[preferences:set-default]
516                 to be called even after a snapshot has been grabbed.}]
517   })
518  
519   (proc-doc/names
520    preferences:default-set?
521    (-> symbol? boolean?)
522    (pref)
523    @{Returns @racket[#t] if @racket[pref] has been passed to
524              @racket[preferences:set-default], @racket[#f]
525              otherwise})
526  
527   (proc-doc/names
528    preferences:set-un/marshall
529    (symbol? (any/c . -> . printable/c) (printable/c . -> . any/c) . -> . void?)
530    (symbol marshall unmarshall)
531    @{@racket[preferences:set-un/marshall] is used to specify marshalling and
532      unmarshalling functions for the preference
533      @racket[symbol]. @racket[marshall] will be called when the users saves their
534      preferences to turn the preference value for @racket[symbol] into a
535      printable value. @racket[unmarshall] will be called when the user's
536      preferences are read from the file to transform the printable value
537      into its internal representation. If @racket[preferences:set-un/marshall]
538      is never called for a particular preference, the values of that
539      preference are assumed to be printable.
540  
541      If the unmarshalling function returns a value that does not meet the
542      guard passed to @racket[preferences:set-default]
543      for this preference, the default value is used.
544  
545      The @racket[marshall] function might be called with any value returned
546      from @racket[read] and it must not raise an error
547      (although it can return arbitrary results if it gets bad input). This might
548      happen when the preferences file becomes corrupted, or is edited
549      by hand.
550  
551      @racket[preferences:set-un/marshall] must be called before calling
552      @racket[preferences:get],@racket[preferences:set].
553  
554      See also @racket[serialize] and @racket[deserialize].
555     })
556  
557   (proc-doc/names
558    preferences:restore-defaults
559    (-> void?)
560    ()
561    @{@racket[(preferences:restore-defaults)] restores the users' configuration
562      to the default preferences.})
563  
564   (proc-doc/names
565    preferences:register-save-callback
566    (-> (-> boolean? any) symbol?)
567    (callback)
568    @{Registers @racket[callback] to run twice for each call
569   to @racket[preferences:set]---once before the preferences
570   file is written, with @racket[#t], and once after it is
571   written, with @racket[#f]. Registration returns a key for
572   use with @racket[preferences:unregister-save-callback].
573   Caveats: @itemize{
574    @item{The callback occurs on whichever
575     thread happened to call @racket[preferences:set].
576    }
577    @item{
578     Pre- and post-write notifications are not necessarily
579     paired; unregistration may cancel the post-write
580     notification before it occurs.}}})
581  
582   (proc-doc/names
583    preferences:unregister-save-callback
584    (-> symbol? void?)
585    (key)
586    @{Unregisters the save callback associated with @racket[key].})
587  
588   (proc-doc/names
589    exn:make-unknown-preference
590    (string? continuation-mark-set? . -> . exn:unknown-preference?)
591    (message continuation-marks)
592    @{Creates an unknown preference exception.})
593  
594   (proc-doc/names
595    exn:unknown-preference?
596    (any/c . -> . boolean?)
597    (exn)
598    @{Determines if a value is an unknown preference exn.})
599  
600   (thing-doc
601    exn:struct:unknown-preference
602    struct-type?
603    @{The struct type for the unknown preference exn.})
604  
605  
606   (parameter-doc
607    preferences:low-level-put-preferences
608    (parameter/c (-> (listof symbol?) (listof any/c) any))
609    put-preferences
610    @{This parameter's value is called to save preference the preferences file.
611      Its interface should be just like mzlib's @racket[put-preferences].
612  
613      The default value calls @racket[put-preferences] and, if there is an error,
614      then starts using a hash-table to save the preferences instead.
615      See also @racket[]})
616  
617   (parameter-doc
618    preferences:low-level-get-preference
619    (parameter/c (->* (symbol?) [(-> any)] any))
620    get-preference
621    @{This parameter's value is called to get a preference from the preferences
622      file. Its interface should be just like @racket[get-preference].
623  
624      The default value calls @racket[get-preferences] and, if there is an error,
625      then starts using a hash-table to save the preferences instead.})
626  
627   (proc-doc/names
628    preferences:snapshot?
629    (-> any/c boolean?)
630    (arg)
631    @{Determines if its argument is a preferences snapshot.
632  
633      See also @racket[preferences:get-prefs-snapshot] and
634      @racket[preferences:restore-prefs-snapshot].})
635   (proc-doc/names
636    preferences:restore-prefs-snapshot
637    (-> preferences:snapshot? void?)
638    (snapshot)
639    @{Restores the preferences saved in @racket[snapshot], updating
640      all of the preferences values to the ones they had at the time
641      that @racket[preferences:get-prefs-snapshot] was called.
642  
643      See also @racket[preferences:get-prefs-snapshot].})
644  
645   (proc-doc/names
646    preferences:get-prefs-snapshot
647    (-> preferences:snapshot?)
648    ()
649    @{Caches all of the current values of the known preferences and returns them.
650      For any preference that has marshalling and unmarshalling set
651      (see @racket[preferences:set-un/marshall]), the preference value is
652      copied by passing it through the marshalling and unmarshalling process.
653      Other values are not copied, but references to them are instead saved.
654  
655      See also @racket[preferences:restore-prefs-snapshot].})
656  
657   (proc-doc/names
658    preferences:new-layer
659    (-> (or/c #f preferences:layer?) preferences:layer?)
660    (previous-preferences-layer)
661    @{Creates a @tech{preferences layer} that extends @racket[previous-preferences-layer].
662  
663    @history[#:added "1.30"]})
664  
665   (proc-doc/names
666    preferences:layer?
667    (-> any/c boolean?)
668    (v)
669    @{Determines if @racket[v] is a @deftech{preferences layer}.
670  
671    A preferences layer gives a form of scoping to preferences. When
672    a new preference is first registered with this library (via a call to
673    @racket[preferences:set-default] or @racket[preferences:add-callback])
674    it is put into the layer in @racket[preferences:current-layer]
675    (and not into any of that layer's previous layers).
676    When @racket[preferences:get], @racket[preferences:set],
677    @racket[preferences:set-un/marshall] are called, they consult and
678    manipulate only the layer where the preference was first installed.
679    Accordingly, preference layers give a way to discard some set of
680    calls to @racket[preference:set-default] and other preference configuration
681    and to start over with a new set. Note that this affects only the configuration
682    of the preferences for the library; the values are all stored centrally
683    (see @racket[preferences:low-level-put-preferences]) and are unaffected
684    by the layers.
685  
686    @examples[#:eval pref-layer-eval
687  
688              (define original-layer (preferences:current-layer))
689  
690              (define layer2 (preferences:new-layer original-layer))
691              (parameterize ([preferences:current-layer layer2])
692                (code:comment "initialize 'a-pref in layer2")
693                (preferences:set-default 'a-pref 5 real?)
694                (preferences:set 'a-pref 6)
695                (preferences:get 'a-pref))
696  
697              (define layer3 (preferences:new-layer original-layer))
698              (parameterize ([preferences:current-layer layer3])
699                (code:comment "initialize 'a-pref again, this time in layer3")
700                (code:comment "without the new layer in place, this would be an error")
701                (preferences:set-default 'a-pref 5 real?)
702                (code:comment "the actual value of the preference remains")
703                (code:comment "from the previous call to preferences:set")
704                (preferences:get 'a-pref))]
705  
706    @history[#:added "1.30"]
707    })
708  
709   (parameter-doc
710    preferences:current-layer
711    (parameter/c preferences:layer?)
712    preferences-layer
713    @{Determines the current @tech{preferences layer}.
714     @history[#:added "1.30"]})
715   )