Transparency/tinting/opacity for layers
This is probably not implemented yet but maybe I'm missing something:
I'm using layers to draw overlay windows ("popups") to ask the user for input. It would be cool if this could "gray out" / tint the layer behind it to make it more obvious to the user that they cannot interact with that layer while the overlay is active.
This can of course be emulated by having a variant (say .inactive) for each attr that uses a different color when that attr is unavailable. But ideally, there would be a global option that mixes each and every color on the inactive layer with gray to achieve this globally.
Interestingly enough, I've wanted to do this exact same thing before! Here's how I do it: when the pop-up window is being displayed, I wrap the lower layer (the UI that will be "grayed out") with forceAttr $ attrName "invalid". That means that the whole UI will be drawn with that attribute, but since it's an invalid attribute name that doesn't correspond to an entry in the attribute map, it will default to the foreground and background color of the terminal. If you wanted a specific attribute, you could add an entry and use its name, but the key here is to use forceAttr. So something like:
draw :: MyState -> [Widget Name]
draw st =
[ popUpWindow st
, forceAttr (attrName "invalid") $ mainUI st
]
Let me know if that gets you what you were after and we can close this or discuss further.
Oooh that's a nice hack! Yes, should get me a decent 80/20. Or one could force a configured global "inactive" attr (which does exist) in the same way, right?
This is not perfect ofc because the colors are now all the same: if I have (say) a red and a green element, they don't go to faded-red and faded-green, but they both go to (say) gray.
Maybe I can actually programmatically patch the attr map on the inactive layer (replacing all colors by a manipulated version) to get the effect I want?
Or one could force a configured global "inactive" attr (which does exist) in the same way, right?
Yes, that's right.
This is not perfect ofc because the colors are now all the same: if I have (say) a red and a green element, they don't go to faded-red and faded-green, but they both go to (say) gray.
Yes, and I'm afraid that I'm not up for implementing full-blown compositing in vty. :) But if someone wanted to, I'd be happy to consider patches!
Maybe I can actually programmatically patch the attr map on the inactive layer (replacing all colors by a manipulated version) to get the effect I want?
Yes: this is exactly why appAttrMap is a function, not a constant.
Maybe I can actually programmatically patch the attr map on the inactive layer (replacing all colors by a manipulated version) to get the effect I want?
Although having this effect apply only to one layer will be tricky if not impossible.
It occurs to me that if your attribute map entries use RGB colors, you could definitely programmatically subdue those. So as long as your underlying UI uses those entries and your pop-up uses a separate set of map entries that aren't subdued, that technique would work nicely.
Although having this effect apply only to one layer will be tricky if not impossible.
I was mistaken about this! I forgot that there is a function updateAttrMap that you can use on a Widget to completely customize the entire attribute map, which would allow you to make whole-map transformations on a per-layer basis:
https://hackage-content.haskell.org/package/brick-2.10/docs/Brick-Widgets-Core.html#v:updateAttrMap
@sschuldenzucker Let me know if you need more assistance; if not, I'll close this.
@jtdaugherty didn't get to actually trying this out this out fully yet but I think it should be fine. Ok to close. Thank you!
Btw, I played around with this a little bit though and one issue I noticed is that the AttrMap constructors are not exported, which means I can't iterate over attributes/values in updateAttrMap, for example. I hacked something together with unsafeCoerce and it's working fine but obviously this is not the nicest way of doing things. Maybe these constructors should be exported or there should be some helper functions to let you look inside the map (with some not-so-pretty default if it's Force).
Depending on what you are doing, it's probably already possible with the existing API. While one could think of this as a transformation on the existing attribute map, it might be more straightforward to think of it instead as a map replacement. In that case, you'd do updateAttrMap (const newMap), with the idea being that newMap is already something you can build: you built your original attribute map, and you could keep its "dimmed" version nearby, called newMap here, and then override the map entirely this way rather than transforming the map in the rendering context.
If that doesn't get you what you want, I'd be happy to consider adding some kind of transformation function to the API.