Scripting help: showing one layer hides another?

This is my second post here. As with my first, I couldn’t decide whether I should put this in Help, in Scripts & Extensions, or in Development, so I apologize if it’s in the wrong place.

In my other post asking for scripting help, I asked for help in setting up a system that would allow me to assign tags to layers, and then toggle the visibility of every layer with a specified tag with a single button press. This second feature I want to script for myself is related but separate: I would like to make it so that if I show a layer with some tag, it hides all the other layers with that same tag.

For example, if I’m making a character, and I’m making various shirts for them, I would like to be able to tag every shirt layer with the shirt tag. Then, if I show a shirt layer, it will automatically hide all the other shirt layers. (I wouldn’t necessarily want it to do that all the time, maybe only if I’m holding some modifier key while I show the layer.)

In order to do this, I need to be able to detect when a layer’s visibility has changed. I know that there are the beforecommand and aftercommand events I can use with the app.command.LayerVisibility command, but this only comes into play when I use that specific command, either with its hotkey or with the Layer > Visible menu item, and it can only affect the current layer. I would like this feature to be able to come into effect when any layer’s visibility is toggled in any way, in particular via the hide/show button on the Timeline.

The specific help I’m looking for here is, I’d like help in determining when a layer’s visibility has changed, and which layer it was. If we had some kind of layerWasHidden and layerWasShown events that I could just listen to, then this would be a breeze, but I don’t think we have that. (Or, if we do, I can’t find it anywhere.) So, one approach that comes to mind is just to keep a table of every layer and its visibility, and have a function that scans through all the layers and compares each one’s current visibility to what I have stored in the table. If it’s now visible but it wasn’t before, then call my LayerWasShown function, and vice versa for the LayerWasHidden function. After I do this for all the layers, I replace the data in the table with their current values. Then I just do this process periodically, say several times a second. I think this approach would work, but it might be a pain, and it certainly wouldn’t be the most elegant solution if something else exists that’s closer to the event-based approach I was hoping for.

Thanks in advance for any insight and advice!

P.S. side note: in my other post, the idea I was talking about doesn’t have any kind of exclusivity to the tags the same way that this idea does. In other words, I might have, for example, an accessory tag that I can use for bracelets, necklaces, etc., and there’s no inherent reason why you shouldn’t be able to see multiple of those at the same time, but then I might have a more specific bracelet tag, and maybe that tag should be exclusive, like maybe you only want to see one bracelet at a time. So I figure, I can specify these tags in different ways, like in the User Data field for a bracelet layer, I could put, tag: clothing, tag: accessory, xtag: bracelet, where “xtag” means “exclusive tag”, something like that.

It is possible, you can use the Timer (introduced in v1.3-rc1) to check X times/second in the background whether or not any layer’s visibility changed. However, you will need to track EVERYTHING yourself, which means for each sprite you will have to keep track of what layers are visible and compare constantly. This will probably get tricky when you also start adding/removing layers while the script is running.

Introducing tags for layers is also entirely possible, but it would be a fairly robust system that I think would achieve very little in comparison to the complexity of the solution. It would introduce another dimension to the Aseprite’s timeline, and while very interesting I don’t think it’s going to pay off for the efforts.

If I could recommend a solution, I’d say stick as close to the established abstractions that Aseprite already provides and build on top of them - your goal here, as I understand, is to show only one of the multiple layers with a single command. In my opinion, you can do that by putting all of these layers into a group and implementing a simple extension that will introduce some Show Only option to Aseprite, you can use a keyboard shortcut relating to the built-in Shift+X that already toggles layer’s visibility and assign this new command to Shift+Alt+X for example. Then you only need a simple function that loops through all selected layers (app.range.layers), grabs their parent (which is a sprite or another layer/group if they are nested), grabs the parent’s nested layers and iterate through these - if a layer is among the selected ones, it’s visible, otherwise, it’s hidden.

You’ll probably still end up with a bit messy loop-in-a-loop-in-a-loop kind of code but it’s going to be a level of magnitude simpler than creating a new system to keep track of layer tags. :v:

1 Like