@@ -312,8 +312,8 @@ Globally named [`Face`](@ref)s.
312312(potentially modified) set of faces. This two-set system allows for any 
313313modifications to the active faces to be undone. 
314314""" 
315- const  FACES =  let  default  =  Dict {Symbol, Face} (
316-     #  Default  is special, it must be completely specified
315+ const  FACES =  let  base  =  Dict {Symbol, Face} (
316+     #  Base  is special, it must be completely specified
317317    #  and everything inherits from it.
318318    :default  =>  Face (
319319        " monospace"  , 120 ,      #  font, height
@@ -350,7 +350,7 @@ const FACES = let default = Dict{Symbol, Face}(
350350    :bright_white  =>  Face (foreground= :bright_white ),
351351    #  Useful common faces
352352    :shadow  =>  Face (foreground= :bright_black ),
353-     :region  =>  Face (background= 0x3a3a3a ),
353+     :region  =>  Face (background= 0x636363 ),
354354    :emphasis  =>  Face (foreground= :blue ),
355355    :highlight  =>  Face (inherit= :emphasis , inverse= true ),
356356    :code  =>  Face (foreground= :cyan ),
@@ -382,6 +382,12 @@ const FACES = let default = Dict{Symbol, Face}(
382382    :repl_prompt_pkg  =>  Face (inherit= [:blue , :repl_prompt ]),
383383    :repl_prompt_beep  =>  Face (inherit= [:shadow , :repl_prompt ]),
384384    )
385+     light =  Dict {Symbol, Face} (
386+         :region  =>  Face (background= 0xaaaaaa ),
387+     )
388+     dark =  Dict {Symbol, Face} (
389+         :region  =>  Face (background= 0x363636 ),
390+     )
385391    basecolors =  Dict {Symbol, RGBTuple} (
386392        :background      =>  (r =  0xff , g =  0xff , b =  0xff ),
387393        :foreground      =>  (r =  0x00 , g =  0x00 , b =  0x00 ),
@@ -401,20 +407,23 @@ const FACES = let default = Dict{Symbol, Face}(
401407        :bright_magenta  =>  (r =  0xbf , g =  0x60 , b =  0xca ),
402408        :bright_cyan     =>  (r =  0x26 , g =  0xc6 , b =  0xda ),
403409        :bright_white    =>  (r =  0xf6 , g =  0xf5 , b =  0xf4 ))
404-     (; default, basecolors,
405-      current =  ScopedValue (copy (default)),
406-      modifications =  ScopedValue (Dict {Symbol, Face} ()),
410+     (themes =  (; base, light, dark),
411+      modifications =  (base =  Dict {Symbol, Face} (), light =  Dict {Symbol, Face} (), dark =  Dict {Symbol, Face} ()),
412+      current =  ScopedValue (copy (base)),
413+      basecolors =  basecolors,
407414     lock =  ReentrantLock ())
408415end 
409416
410417# # Adding and resetting faces ##
411418
412419""" 
413-     addface!(name::Symbol => default::Face) 
420+     addface!(name::Symbol => default::Face, theme::Symbol = :base ) 
414421
415422Create a new face by the name `name`. So long as no face already exists by this 
416- name, `default` is added to both `FACES``.default` and (a copy of) to 
417- `FACES`.`current`, with the current value returned. 
423+ name, `default` is added to both `FACES.themes[theme]` and (a copy of) to 
424+ `FACES.current`, with the current value returned. 
425+ 
426+ The `theme` should be either `:base`, `:light`, or `:dark`. 
418427
419428Should the face `name` already exist, `nothing` is returned. 
420429
@@ -427,11 +436,12 @@ Face (sample)
427436     underline: true 
428437``` 
429438""" 
430- function  addface! ((name, default):: Pair{Symbol, Face} )
431-     @lock  FACES. lock if  ! haskey (FACES. default, name)
432-         FACES. default[name] =  default
433-         FACES. current[][name] =  if  haskey (FACES. current[], name)
434-             merge (copy (default), FACES. current[][name])
439+ function  addface! ((name, default):: Pair{Symbol, Face} , theme:: Symbol  =  :base )
440+     current =  FACES. current[]
441+     @lock  FACES. lock if  ! haskey (FACES. themes[theme], name)
442+         FACES. themes[theme][name] =  default
443+         current[name] =  if  haskey (current, name)
444+             merge (copy (default), current[name])
435445        else 
436446            copy (default)
437447        end 
@@ -447,10 +457,12 @@ function resetfaces!()
447457    @lock  FACES. lock begin 
448458        current =  FACES. current[]
449459        empty! (current)
450-         for  (key, val) in  FACES. default 
460+         for  (key, val) in  FACES. themes . base 
451461            current[key] =  val
452462        end 
453-         empty! (FACES. modifications[])
463+         if  current ===  FACES. current. default #  Only when top-level
464+             map (empty!, values (FACES. modifications))
465+         end 
454466        current
455467    end 
456468end 
@@ -464,13 +476,15 @@ If the face `name` does not exist, nothing is done and `nothing` returned.
464476In the unlikely event that the face `name` does not have a default value, 
465477it is deleted, a warning message is printed, and `nothing` returned. 
466478""" 
467- function  resetfaces! (name:: Symbol )
468-     @lock  FACES. lock if  ! haskey (FACES. current[], name)
469-     elseif  haskey (FACES. default, name)
470-         delete! (FACES. modifications[], name)
471-         FACES. current[][name] =  copy (FACES. default[name])
479+ function  resetfaces! (name:: Symbol , theme:: Symbol  =  :base )
480+     current =  FACES. current[]
481+     @lock  FACES. lock if  ! haskey (current, name) #  Nothing to reset
482+     elseif  haskey (FACES. themes[theme], name)
483+         current ===  FACES. current. default && 
484+             delete! (FACES. modifications[theme], name)
485+         current[name] =  copy (FACES. themes[theme][name])
472486    else  #  This shouldn't happen
473-         delete! (FACES . current[] , name)
487+         delete! (current, name)
474488        @warn  """ The face $name  was reset, but it had no default value, and so has been deleted instead!,
475489                 This should not have happened, perhaps the face was added without using `addface!`?"""  
476490    end 
@@ -656,18 +670,17 @@ Face (sample)
656670    foreground: #ff0000 
657671``` 
658672""" 
659- function  loadface! ((name, update):: Pair{Symbol, Face} )
673+ function  loadface! ((name, update):: Pair{Symbol, Face} , theme :: Symbol   =   :base )
660674    @lock  FACES. lock begin 
661-         mface =  get (FACES. modifications[], name, nothing )
662-         if  ! isnothing (mface)
663-             update =  merge (mface, update)
664-         end 
665-         FACES. modifications[][name] =  update
666-         cface =  get (FACES. current[], name, nothing )
667-         if  ! isnothing (cface)
668-             update =  merge (cface, update)
675+         current =  FACES. current[]
676+         if  FACES. current. default ===  current #  Only save top-level modifications
677+             mface =  get (FACES. modifications[theme], name, nothing )
678+             isnothing (mface) ||  (update =  merge (mface, update))
679+             FACES. modifications[theme][name] =  update
669680        end 
670-         FACES. current[][name] =  update
681+         cface =  get (current, name, nothing )
682+         isnothing (cface) ||  (update =  merge (cface, update))
683+         current[name] =  update
671684    end 
672685end 
673686
682695
683696For each face specified in `Dict`, load it to `FACES``.current`. 
684697""" 
685- function  loaduserfaces! (faces:: Dict{String, Any} , prefix:: Union{String, Nothing} = nothing )
698+ function  loaduserfaces! (faces:: Dict{String, Any} , prefix:: Union{String, Nothing} = nothing , theme:: Symbol  =  :base )
699+     theme ==  :base  &&  prefix ∈  map (String, setdiff (keys (FACES. themes), (:base ,))) && 
700+         return  loaduserfaces! (faces, nothing , Symbol (prefix))
686701    for  (name, spec) in  faces
687702        fullname =  if  isnothing (prefix)
688703            name
@@ -692,9 +707,9 @@ function loaduserfaces!(faces::Dict{String, Any}, prefix::Union{String, Nothing}
692707        fspec =  filter ((_, v):: Pair  ->  ! (v isa  Dict), spec)
693708        fnest =  filter ((_, v):: Pair  ->  v isa  Dict, spec)
694709        ! isempty (fspec) && 
695-             loadface! (Symbol (fullname) =>  convert (Face, fspec))
710+             loadface! (Symbol (fullname) =>  convert (Face, fspec), theme )
696711        ! isempty (fnest) && 
697-             loaduserfaces! (fnest, fullname)
712+             loaduserfaces! (fnest, fullname, theme )
698713    end 
699714end 
700715
@@ -781,23 +796,60 @@ function recolor(f::Function)
781796    nothing 
782797end 
783798
799+ """ 
800+     setcolors!(color::Vector{Pair{Symbol, RGBTuple}}) 
801+ 
802+ Update the known base colors with those in `color`, and recalculate current faces. 
803+ 
804+ `color` should be a complete list of known colours. If `:foreground` and 
805+ `:background` are both specified, the faces in the light/dark theme will be 
806+ loaded. Otherwise, only the base theme will be applied. 
807+ """ 
784808function  setcolors! (color:: Vector{Pair{Symbol, RGBTuple}} )
785-     @lock  recolor_lock begin 
809+     lock (recolor_lock)
810+     lock (FACES. lock)
811+     try 
812+         #  Apply colors
813+         fg, bg =  nothing , nothing 
786814        for  (name, rgb) in  color
787815            FACES. basecolors[name] =  rgb
816+             if  name ===  :foreground 
817+                 fg =  rgb
818+             elseif  name ===  :background 
819+                 bg =  rgb
820+             end 
821+         end 
822+         newtheme =  if  isnothing (fg) ||  isnothing (bg)
823+             :unknown 
824+         else 
825+             ifelse (sum (fg) >  sum (bg), :dark , :light )
788826        end 
827+         #  Reset all themes to defaults
789828        current =  FACES. current[]
790-         for  ( name, _) in  FACES. modifications[]
791-             default =  get (FACES. default , name, nothing )
829+         for  theme  in   keys (FACES . themes), ( name, _) in  FACES. modifications[theme ]
830+             default =  get (FACES. themes . base , name, nothing )
792831            isnothing (default) &&  continue 
793832            current[name] =  default
794833        end 
834+         if  newtheme ∈  keys (FACES. themes)
835+             for  (name, face) in  FACES. themes[newtheme]
836+                 current[name] =  merge (current[name], face)
837+             end 
838+         end 
839+         #  Run recolor hooks
795840        for  hook in  recolor_hooks
796841            hook ()
797842        end 
798-         for  (name, face) in  FACES. modifications[]
799-             current[name] =  merge (current[name], face)
843+         #  Layer on modifications
844+         for  theme in  keys (FACES. themes)
845+             theme ∈  (:base , newtheme) ||  continue 
846+             for  (name, face) in  FACES. modifications[theme]
847+                 current[name] =  merge (current[name], face)
848+             end 
800849        end 
850+     finally 
851+         unlock (FACES. lock)
852+         unlock (recolor_lock)
801853    end 
802854end 
803855
0 commit comments