How would you get the working (active) layer from an embedded script?

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

How would you get the working (active) layer from an embedded script?

Post by Rai López »

Hi, this may sound like a silly question at first sight at least (and luckily it finally turn to be indeed a silly question), but... Do you think there is a reliable way to get the currently active/working layer in palette from a layer script embedded into another layer anywhere in project? The problem I'm facing is that when used from an embedded script, moho.layer (which is what you'd use for this goal e. g. from a tool/menu script) ALWAYS return the script hosting layer no matter which layer(s) is/are selected in palette, so I don't see how I can trust on it for this purpose.

Besides that, there are at least a couple of functions that I thought could help; for example, I've been trying to use this: moho.document:GetSelectedLayer(0) but, besides it sometimes returns nil randomly I don't know why, it only returns the main working layer as long there is only one layer selected or it is the bottom one of a multi selection.

The other candidate I thought is MohoLayer:SecondarySelection(), but as stayed there, it only says if the layer in question is selected or not, not giving any clue about if it is the working (active) one or any other under a possible multi-selection scenario...

So I'm not finding the way to really can do it, but OTOH it's one of those so basic things that I hope I'm simply missing something or it is in front of me and for some reason I'm overlooking it... In any case, since four eyes see more than two, thanks in advance for any input on the right direction if possible!
...
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

🤔 I've just seen it and I'm wondering if MohoDoc:ClearSecondarySelection() could be the key... But it would imply to store in a table the currently multi layer selection, run the function so that only the working layer remain selected, get it with "moho.document:GetSelectedLayer(0)" now that it's the only one selected, and then restore the multi-selection in base of what was stored before so that it remains the same and, ideally, as if nothing really happened under the hood...

Well, that assuming MohoDoc:ClearSecondarySelection(group) doesn't end up complicating things too much if selected layers are mixed in root or inside/outside of a group, etc. In the absence of anything else for now, I'll start to experiment with this, but I still really hope there is a less convoluted way to get it that for some reason I'm not seeing, or at least something that doesn't imply go deselecting and reselecting layers on the fly while other indirectly involved processes well could be somehow affected.
...
User avatar
synthsin75
Posts: 10016
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by synthsin75 »

Usually this is done by finding a layer by another means, like layer name, suffix, tag, etc.. If you know where it is in relation to the embedded layer, that can help.

But all you need is anything that returns a MohoLayer object. In many cases, you'll need to search the whole document: https://mohoscripting.com/snippets/1
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

Hi and thanks, Wes!

But in this case it could be any layer along the whole project and I just need to know which is the working/active no matter how many of them are selected and in base of no other parameter... so I'm afraid, in the end, I'm kinda screwed?

About parsing all the layers, I've thought on it, but I can't figure out yet what could be the "trigger" for, once the loop get the working layer, return it *unequivocally* in base of... well, in base of something I still can't figure out (it's like I got looped :lol:).

I'm still experimenting with what I commented about deselecting/reselecting, but for some reason I'm not getting results yet... This is where I am right now anyway:

Code: Select all

function LayerScript(moho)
	local doc = moho.document
	local scriptLayer = moho.layer
	local workingLayer = doc:GetSelectedLayer(0)
	local selCount = doc:CountSelectedLayers()
	local selLayers = {}

	if selCount > 1 then
		for i = 0, selCount - 1 do
			local layer = doc:GetSelectedLayer(i)
			table.insert(selLayers, layer)
		end

		doc:ClearSecondarySelection()
		workingLayer = doc:GetSelectedLayer(0) --print(workingLayer:Name())

		for i = 1, #selLayers do
			local layer = selLayers[i]
			layer:SetSecondarySelection(true)
		end
	end

	print(workingLayer:Name())
end


It's just quick/dirty testing and it's late here so maybe I'm, again, overlooking something, but for some reason "workingLayer = doc:GetSelectedLayer(0)" is still giving me the bottom layer of a multi-selection instead what I expected it would give me after clearing secondary selection. It may be just a matter of force some kind of update in-between or something, but I still don't see why it should be necessary here, although I hope some more testing end up throwing some light around it...
...
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

OK, it seems as soon as I insert something like "doc:CountSelectedLayers()" in-between "doc:ClearSecondarySelection()" and "workingLayer = doc:GetSelectedLayer(0)" I start getting some results, results that I'll have to stress and see, but at least it's something...

Code: Select all

function LayerScript(moho)
	local doc = moho.document
	local scriptLayer = moho.layer
	local workingLayer = doc:GetSelectedLayer(0)
	local selCount = doc:CountSelectedLayers()
	local selLayers = {}

	if selCount > 1 then
		for i = 0, selCount - 1 do --storing current multi-selection
			local layer = doc:GetSelectedLayer(i)
			table.insert(selLayers, layer)
		end

		doc:ClearSecondarySelection() --clearing current multi-selection
		doc:CountSelectedLayers() --THIS SEEMS TO MAKE IT WORK FOR WHAEVER REASON...
		workingLayer = doc:GetSelectedLayer(0) --getting the working layer now that it's the only one selected

		for i = 1, #selLayers do --restoring the previous multi-selection
			local layer = selLayers[i]
			layer:SetSecondarySelection(true)
		end
	end

	print(workingLayer:Name())
end

It seems it was indeed an internal update requirement for whatever reason. Or maybe the GetSelectedLayer() method is somehow buggy and that's why it sometimes returns nil randomly and for no reason (thing that I'll still have to figure out how to solve if I want it to be really reliable, BTW).

Again, I didn't expect having to resort to something like this for a so primary necessity as is to know which one is the working layer... but if you agree there is really no easy way to do it in this embedded script cases, at least I can continue working without feeling so stupid as I was feeling before, which is also something :roll:
...
User avatar
synthsin75
Posts: 10016
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by synthsin75 »

I forgot that you said you needed to find the selected layer. If it wasn't the primary layer of multiple selected, it would be easy.

I always expect SecondarySelection to only be true for selected layers that aren't the active layer.

Seems like you're on the right path.
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

synthsin75 wrote: Tue Feb 21, 2023 6:56 am I always expect SecondarySelection to only be true for selected layers that aren't the active layer.
Yeah, that would be SO desirable... It would make everything easy peasy in this cases, and indeed (besides the name itself) for the way it's officially used in "lm_randompointcolors.lua":

Code: Select all

if (layer:LayerType() == MOHO.LT_VECTOR and (layer:SecondarySelection() or layer == moho.layer)) then
One would say that's they way it should work, isn't? So I may end up reporting it or, if not, proposing it as feature request instead.

Well, let's see if I can solve now the other problem regarding "doc:GetSelectedLayer(0)" randomly returning nil for no apparent reason (although it seem it happens often upon returning focus to Moho's main window), I'm trying some weird workarounds like e. g. forcing it to found something by means of a while block like this:

Code: Select all

while workLayer == nil do
	doc:CountSelectedLayers() --THIS SEEMS TO MAKE IT WORK FOR WHATEVER REASON...
	workLayer = doc:GetSelectedLayer(0) --getting the working layer now that it's the only one selected while trying to ensure it's not nil?
	workLayerID = doc:LayerAbsoluteID(workLayer)
end
But again I'm not getting yet the expected results and, even if it worked, I'm not totally sure it's a very good idea for the risk of endless loops, although I might implement a limit of tries or something, but in any case that will have to be tomorrow... Well, thank you so much for being there again!
...
User avatar
hayasidist
Posts: 3566
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: How would you get the working (active) layer from an embedded script?

Post by hayasidist »

There is something not consistent with "getSelectedLayer(i)" -- the first (i=0) IMO ought always to be the "highlighted" layer in the layer panel. It is not always so, as it seems to depend on the order in which layers are manually selected.

Using this test file, and selecting multiple layers, sometimes by click then shift-click to select a range, sometimes by ctrl-click adding layers to the selection ...
Group
>> multiple child layers
Vector
Vector
Vector with layerscript.


... this layerscript ...

Code: Select all

function LayerScript(moho)
	local ctSel = moho.document:CountSelectedLayers()
	local i, layer
	if ctSel > 0 then
		for i = 0, ctSel-1 do
			layer = moho.document:GetSelectedLayer(i)
			print ("selected layer ", i, " ", layer:Name())
		end
	end
end
... correctly identifies all the selected layers, but shows the issue.

ofc if there's only one selected layer it works fine.

===

as an aside: I do recall adding a note somewhere in mohoscripting about needing to count layers before using an index but I can't find it right now... I'll update this if /when I do...
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

Hi, Paul, and thank you for taking the time of testing it! It's curious there is a so notorious inconsistency in a method widely used by the official tools, so I'm starting to think the problem could only arise while used by an embedded script, for some reason... Another curious thing about I'm seeing now is "doc:GetSelectedLayer(0)" is returning nil here precisely when there is only one layer selected (and that would explain why official tools don't show up the issue). What I'm starting to think according to my latest tests is that, for some reason, even if there is clearly several layers selected in palette, at some script run cycle Moho "thinks" there is only one and that's what could have been messing things... At least that's what it seems at first sight according to this:

Image

And with basically the same code as before, but here it's again for more clarity:

Code: Select all

function LayerScript(moho)
	local doc = moho.document
	local scriptLayer = moho.layer
	local workLayer = nil
	local workLayerID = nil
	local selCount = doc:CountSelectedLayers()
	local selLayers = {}

	if selCount > 1 then
		for i = 0, selCount - 1 do --storing current multi-selection
			local layer = doc:GetSelectedLayer(i)
			table.insert(selLayers, layer)
		end
		doc:ClearSecondarySelection() --clearing current multi-selection

		--while workLayer == nil do
			doc:CountSelectedLayers() --THIS SEEMS TO MAKE IT WORK FOR WHATEVER REASON...
			workLayer = doc:GetSelectedLayer(0) --getting the working layer now that it's the only one selected while trying to ensure it's not nil?
			workLayerID = doc:LayerAbsoluteID(workLayer)
			moho:SetSelLayer(workLayer, true, true) --layer, multiSelect, allowDeselect
		--end

		for i = 1, #selLayers do --restoring the previous multi-selection
			local layer = selLayers[i]
			layer:SetSecondarySelection(true)
		end
	else print("ELSE")
		doc:CountSelectedLayers() 
		workLayer = doc:GetSelectedLayer(0)
		workLayerID = doc:LayerAbsoluteID(workLayer)
	end

	print("Selected: " .. doc:CountSelectedLayers())
	print("Working: " .. workLayer:Name())
	--print(doc:LayerByAbsoluteID(workLayerID):Name())
end

So there it is, the "else" block should never have been run since at that point there was more than one layer selected as can bee seen, but it seems at times "doc:CountSelectedLayers()" returns that only one is selected (or even NO ONE as I recall have seen before, but I still have to test that) and, in this case, caused nothing I tried before above seemed to really take any effect... So, well, knowing that I'm going to continue testing and see...

EDIT: Effectively, at the point the script throws the error "doc:CountSelectedLayers()" returns "0" whereas, supposedly, there should be always at least 1 layer selected in Moho all the time, at least in terms of what it's shown by the UI... Well, let's see if from now on the awareness of that somehow easies things!
...
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

Well, up to this point I think I'm going to try to go forward with what I have for now... The case is at least the following kind of solution:

Code: Select all

function LayerScript(moho)
	local doc = moho.document
	local scriptLayer = moho.layer
	local workLayer = nil
	local workLayerID = nil
	local selCount = doc:CountSelectedLayers()
	local selLayers = {}

	if selCount > 1 then
		for i = 0, selCount - 1 do --storing current multi-selection
			local layer = doc:GetSelectedLayer(i)
			table.insert(selLayers, layer)
		end

		doc:ClearSecondarySelection() --clearing current multi-selection
		doc:CountSelectedLayers() --THIS DUMMY LINE SEEMS TO MAKE IT WORK FOR WHATEVER REASON...

		workLayer = doc:GetSelectedLayer(0) --getting the working layer now that it's the only one selected
		workLayerID = doc:LayerAbsoluteID(workLayer)
		--moho:SetSelLayer(workLayer, true, true) --layer, multiSelect, allowDeselect

		for i = 1, #selLayers do --restoring the previous multi-selection
			local layer = selLayers[i]
			layer:SetSecondarySelection(true)
		end
	elseif selCount == 1 then print("ELSEIF :" .. doc:CountSelectedLayers())
		workLayer = doc:GetSelectedLayer(0)
		workLayerID = doc:LayerAbsoluteID(workLayer)
	else print("ELSE :" .. doc:CountSelectedLayers())
		--doc:Refresh()
		if prevWorkLayer ~= nil then
			workLayer = prevWorkLayer
		else
			return
		end
	end

	--print(doc:LayerByAbsoluteID(workLayerID):Name())
	print("Selected: " .. doc:CountSelectedLayers())
	print("Working: " .. workLayer:Name())
	prevWorkLayer = workLayer
end
Seems to continue returning the correct layer even after the "doc:CountSelectedLayers()" failure, as can be seen here:

Image

So I'll try to implement something like this and then go seeing if it gives any problem under more stress or real usage... Of course I don't lose hope about someone finding something I'm not being able to for such a mundane task as is getting the working layer, but if that doesn't happen at least I may continue working with this, hopefully. Well, thanks for helping!
...
User avatar
hayasidist
Posts: 3566
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: How would you get the working (active) layer from an embedded script?

Post by hayasidist »

FWIW, I've managed to send moho into a loop by adding a layer at the top of the list. IOW with your layers, add a new vector layer above group Layer 9 and watch the fun. At some point in its looping, it will throw the "nil value workLayer" error...

Probably worth a bug report ...
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

Well, if we start adding layer addition/deletion to the recipe, for my latest experiences on that matter, it doesn't surprise me at all funny things start happen... That's precisely the reason for I wanted to avoid as much as possible having to play too much with layers state only for this, but it seems selecting/deselecting them is actually not causing any problems, fortunately.

What I mean is I'm not certain about if such loop issue you describe is directly related to this or more with the already reported bugs about layers internal management inconsistencies, although it well could be somehow related, of course. In any case, I'll try to report at least the doc:CountSelectedLayers() returning 0 at times issue, because that seems to me like the most certain inconsistency I see around all this and, hopefully, at some point all together help to improve script layers managing reliability in general... I wish!
...
User avatar
synthsin75
Posts: 10016
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by synthsin75 »

Might be easier to keep a running tally of selected layers. If the number increases, the layer newly added to the selection is the active layer. If the number decreases, either there is only one layer selected or one's been deselected. If the latter, the new active layer is either the one below the deselected layer (in layer hierarchy) or the lowest, if the lowest was deselected.

I would just have the layerscript return whenever CountSelectedLayers returns zero.
User avatar
Rai López
Posts: 2253
Joined: Sun Aug 08, 2004 1:41 pm
Location: Spain
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by Rai López »

Indeed, the idea is this piece of code only be run if the number of selected layers change, at first simply with performance reasons in mind but now with more reason for also reducing at minimum the possibilities of getting false CountSelectedLayers() returns, so I could consider to extend the principle up to the point of what you suggest, Wes. It kind of hurt a little to try to take into account all the possibilities but, as you point, they are really not so much... The only exception I'm seeing for this to be totally workable is upon switching top working layer to bottom working layer (and vice-versa) with the shift key pressed under a multi-selection scenario, since the number of selected layers doesn't change in that case (or at least not visually in UI, I may have to test it by a script to see if something changes from there by chance susceptible to be caught).

For now I think I'm going to continue using this method because I feel like advance a little, but as soon as it present some problem or limitation (as I'm afraid will happen at some point as usually end happening with patches), I think I might start to consider that other way around, so thank you for pointing it out!
...
User avatar
synthsin75
Posts: 10016
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: How would you get the working (active) layer from an embedded script?

Post by synthsin75 »

Ramón López wrote: Wed Feb 22, 2023 1:42 am The only exception I'm seeing for this to be totally workable is upon switching top working layer to bottom working layer (and vice-versa) with the shift key pressed under a multi-selection scenario, since the number of selected layers doesn't change in that case (or at least not visually in UI, I may have to test it by a script to see if something changes from there by chance susceptible to be caught).
Damn, I missed that one. I wonder how often anyone does that though.
Post Reply