@@ -403,20 +403,23 @@ const FACES = let default = Dict{Symbol, Face}(
403403 :bright_magenta => (r = 0xbf , g = 0x60 , b = 0xca ),
404404 :bright_cyan => (r = 0x26 , g = 0xc6 , b = 0xda ),
405405 :bright_white => (r = 0xf6 , g = 0xf5 , b = 0xf4 ))
406- (; default, basecolors,
406+ (themes = (base = default, light = Dict {Symbol, Face} (), dark = Dict {Symbol, Face} ()),
407+ modifications = (base = Dict {Symbol, Face} (), light = Dict {Symbol, Face} (), dark = Dict {Symbol, Face} ()),
407408 current = ScopedValue (copy (default)),
408- modifications = ScopedValue ( Dict {Symbol, Face} ()) ,
409+ basecolors = basecolors ,
409410 lock = ReentrantLock ())
410411end
411412
412413# # Adding and resetting faces ##
413414
414415"""
415- addface!(name::Symbol => default::Face)
416+ addface!(name::Symbol => default::Face, theme::Symbol = :base )
416417
417418Create a new face by the name `name`. So long as no face already exists by this
418- name, `default` is added to both `FACES``.default` and (a copy of) to
419- `FACES`.`current`, with the current value returned.
419+ name, `default` is added to both `FACES.themes[theme]` and (a copy of) to
420+ `FACES.current`, with the current value returned.
421+
422+ The `theme` should be either `:base`, `:light`, or `:dark`.
420423
421424Should the face `name` already exist, `nothing` is returned.
422425
@@ -429,11 +432,12 @@ Face (sample)
429432 underline: true
430433```
431434"""
432- function addface! ((name, default):: Pair{Symbol, Face} )
433- @lock FACES. lock if ! haskey (FACES. default, name)
434- FACES. default[name] = default
435- FACES. current[][name] = if haskey (FACES. current[], name)
436- merge (copy (default), FACES. current[][name])
435+ function addface! ((name, default):: Pair{Symbol, Face} , theme:: Symbol = :base )
436+ current = FACES. current[]
437+ @lock FACES. lock if ! haskey (FACES. themes[theme], name)
438+ FACES. themes[theme][name] = default
439+ current[name] = if haskey (current, name)
440+ merge (copy (default), current[name])
437441 else
438442 copy (default)
439443 end
@@ -449,10 +453,12 @@ function resetfaces!()
449453 @lock FACES. lock begin
450454 current = FACES. current[]
451455 empty! (current)
452- for (key, val) in FACES. default
456+ for (key, val) in FACES. themes . base
453457 current[key] = val
454458 end
455- empty! (FACES. modifications[])
459+ if current === FACES. current. default # Only when top-level
460+ map (empty!, values (FACES. modifications))
461+ end
456462 current
457463 end
458464end
@@ -466,13 +472,15 @@ If the face `name` does not exist, nothing is done and `nothing` returned.
466472In the unlikely event that the face `name` does not have a default value,
467473it is deleted, a warning message is printed, and `nothing` returned.
468474"""
469- function resetfaces! (name:: Symbol )
470- @lock FACES. lock if ! haskey (FACES. current[], name)
471- elseif haskey (FACES. default, name)
472- delete! (FACES. modifications[], name)
473- FACES. current[][name] = copy (FACES. default[name])
475+ function resetfaces! (name:: Symbol , theme:: Symbol = :base )
476+ current = FACES. current[]
477+ @lock FACES. lock if ! haskey (current, name) # Nothing to reset
478+ elseif haskey (FACES. themes[theme], name)
479+ current === FACES. current. default &&
480+ delete! (FACES. modifications[theme], name)
481+ current[name] = copy (FACES. themes[theme][name])
474482 else # This shouldn't happen
475- delete! (FACES . current[] , name)
483+ delete! (current, name)
476484 @warn """ The face $name was reset, but it had no default value, and so has been deleted instead!,
477485 This should not have happened, perhaps the face was added without using `addface!`?"""
478486 end
@@ -658,18 +666,17 @@ Face (sample)
658666 foreground: #ff0000
659667```
660668"""
661- function loadface! ((name, update):: Pair{Symbol, Face} )
669+ function loadface! ((name, update):: Pair{Symbol, Face} , theme :: Symbol = :base )
662670 @lock FACES. lock begin
663- mface = get (FACES. modifications[], name, nothing )
664- if ! isnothing (mface)
665- update = merge (mface, update)
666- end
667- FACES. modifications[][name] = update
668- cface = get (FACES. current[], name, nothing )
669- if ! isnothing (cface)
670- update = merge (cface, update)
671+ current = FACES. current[]
672+ if FACES. current. default === current # Only save top-level modifications
673+ mface = get (FACES. modifications[theme], name, nothing )
674+ isnothing (mface) || (update = merge (mface, update))
675+ FACES. modifications[theme][name] = update
671676 end
672- FACES. current[][name] = update
677+ cface = get (current, name, nothing )
678+ isnothing (cface) || (update = merge (cface, update))
679+ current[name] = update
673680 end
674681end
675682
684691
685692For each face specified in `Dict`, load it to `FACES``.current`.
686693"""
687- function loaduserfaces! (faces:: Dict{String, Any} , prefix:: Union{String, Nothing} = nothing )
694+ function loaduserfaces! (faces:: Dict{String, Any} , prefix:: Union{String, Nothing} = nothing , theme:: Symbol = :base )
695+ theme == :base && prefix ∈ map (String, setdiff (keys (FACES. themes), (:base ,))) &&
696+ return loaduserfaces! (faces, nothing , Symbol (prefix))
688697 for (name, spec) in faces
689698 fullname = if isnothing (prefix)
690699 name
@@ -694,7 +703,7 @@ function loaduserfaces!(faces::Dict{String, Any}, prefix::Union{String, Nothing}
694703 fspec = filter ((_, v):: Pair -> ! (v isa Dict), spec)
695704 fnest = filter ((_, v):: Pair -> v isa Dict, spec)
696705 ! isempty (fspec) &&
697- loadface! (Symbol (fullname) => convert (Face, fspec))
706+ loadface! (Symbol (fullname) => convert (Face, fspec), theme )
698707 ! isempty (fnest) &&
699708 loaduserfaces! (fnest, fullname)
700709 end
@@ -784,22 +793,50 @@ function recolor(f::Function)
784793end
785794
786795function setcolors! (color:: Vector{Pair{Symbol, RGBTuple}} )
787- @lock recolor_lock begin
796+ lock (recolor_lock)
797+ lock (FACES. lock)
798+ try
799+ # Apply colors
800+ fg, bg = nothing , nothing
788801 for (name, rgb) in color
789802 FACES. basecolors[name] = rgb
803+ if name === :foreground
804+ fg = rgb
805+ elseif name === :background
806+ bg = rgb
807+ end
790808 end
809+ newtheme = if ! isnothing (fg) && ! isnothing (bg)
810+ :unknown
811+ else
812+ ifelse (sum (fg) > sum (bg), :light , :dark )
813+ end
814+ # Reset all themes to defaults
791815 current = FACES. current[]
792- for ( name, _) in FACES. modifications[]
793- default = get (FACES. default , name, nothing )
816+ for theme in keys (FACES . themes), ( name, _) in FACES. modifications[theme ]
817+ default = get (FACES. themes . base , name, nothing )
794818 isnothing (default) && continue
795819 current[name] = default
796820 end
821+ if newtheme ∈ keys (FACES. themes)
822+ for (name, face) in FACES. themes[newtheme]
823+ current[name] = merge (current[name], face)
824+ end
825+ end
826+ # Run recolor hooks
797827 for hook in recolor_hooks
798828 hook ()
799829 end
800- for (name, face) in FACES. modifications[]
801- current[name] = merge (current[name], face)
830+ # Layer on modifications
831+ for theme in keys (FACES. themes)
832+ theme ∈ (:base , newtheme) || continue
833+ for (name, face) in FACES. modifications[theme]
834+ current[name] = merge (current[name], face)
835+ end
802836 end
837+ finally
838+ unlock (FACES. lock)
839+ unlock (recolor_lock)
803840 end
804841end
805842
0 commit comments