Let's suggest new API features

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

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
hayasidist
Posts: 3565
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Let's suggest new API features

Post by hayasidist »

SimplSam wrote: Sat Mar 13, 2021 6:11 pm Modifier keys

Something else that would be welcomed is if modifier keys were recognised when the tool button was invoked. Even extending to recognising Right Click.

This would allow for options like settings mode and advanced mode etc etc..
that was formally requested back in 2018 ... let's hope the new team are a bit more responsive ...

[Edit]:
I'll reignite that particular flame after 13.5 hits the streets. I think the dev team has got its hands full enough right now ...
Last edited by hayasidist on Mon Mar 15, 2021 4:53 pm, edited 1 time in total.
User avatar
KuzKuz
Posts: 504
Joined: Mon Aug 19, 2013 5:12 pm
Location: Ukraine

Re: Let's suggest new API features

Post by KuzKuz »

SimplSam wrote: Sat Mar 13, 2021 6:11 pm Modifier keys

Something else that would be welcomed is if modifier keys were recognised when the tool button was invoked. Even extending to recognising Right Click.

This would allow for options like settings mode and advanced mode etc etc..
Yes Yes Yes +++
User avatar
synthsin75
Posts: 10013
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Let's suggest new API features

Post by synthsin75 »

Even if just the Alt modifier were available for toolbar buttons, just like we have for GUI buttons: http://mohoscripting.com/methods/1051
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

Stan wrote: Sat Feb 20, 2021 5:40 am
  • Add the moho object to the UpdateWidgets and HandleMessage methods of the non-modal dialogs.
Yes yes yes, this would create tons of possibilities for new tools.
SimplSam wrote: Sun Feb 28, 2021 1:35 pm Expressions
Yesss! This would make some layerscripts obsolete and would be a much user friendlier way to expand rigging possibilities.
synthsin75 wrote: Mon Mar 15, 2021 4:51 pmEven if just the Alt modifier were available for toolbar buttons, just like we have for GUI buttons: http://mohoscripting.com/methods/1051
😳 I didn't even know this was possible!!! Definitely going to implement this right away. It would be nice to have all of them available.
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

Stan wrote: Sat Feb 20, 2021 5:40 am
  • Add the moho object to the UpdateWidgets and HandleMessage methods of the non-modal dialogs.
Maybe I'm not understanding this correctly, but I just read some code and I think it's actually possible to add the moho object to the UpdateWidgets method of a non-modal dialogue... Check out the LM_Set_Origin.lua tool. It actually creates a modeless window in DoLayout, but the code is commented out.

Code: Select all

	--if (self.layerSettingsWnd == nil) then
	--	self.layerSettingsWnd = LM_LayerSettingsDialog:new()
	--	self.layerSettingsWnd:DoModeless()
	--end
And then the tool also contains:

Code: Select all

-- register the layer window to be updated when changes are made
table.insert(MOHO.UpdateTable, LM_LayerSettingsDialog_Update)
Which actually triggers the dialog to update during updates, and it passes moho...?

Code: Select all

function LM_LayerSettingsDialog_Update(moho)
🤓
User avatar
synthsin75
Posts: 10013
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Let's suggest new API features

Post by synthsin75 »

You technically can pass moho to any window through the d.moho = moho variable. But that window will not be able to get changes from Moho, like the current frame, if changed since the window was invoked. You also have to pass any moho methods through their own variable. Assigning d.moho doesn't allow self.moho.layer to work, but assigning d.layer = moho.layer lets self.layer work. So you have to assign every method you want to use. As long as the new() function can accept moho, like new(moho), this works.
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

synthsin75 wrote: Sat Apr 10, 2021 11:04 pmBut that window will not be able to get changes from Moho, like the current frame, if changed since the window was invoked.
Well, the dialog in LM_SetOrigin actually gets changes from Moho after being invoked. Or is that also possible with the method you just mentioned? I think I'm in over my head here...
User avatar
synthsin75
Posts: 10013
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Let's suggest new API features

Post by synthsin75 »

Lukas wrote: Sat Apr 10, 2021 11:16 pm
synthsin75 wrote: Sat Apr 10, 2021 11:04 pmBut that window will not be able to get changes from Moho, like the current frame, if changed since the window was invoked.
Well, the dialog in LM_SetOrigin actually gets changes from Moho after being invoked. Or is that also possible with the method you just mentioned? I think I'm in over my head here...
I have to investigate that further. I've never seen something like a table.insert called outside of a function, which would insert it when the tool was loaded by Moho.
User avatar
hayasidist
Posts: 3565
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Let's suggest new API features

Post by hayasidist »

Lukas wrote: Sat Apr 10, 2021 10:43 pm Maybe I'm not understanding this correctly, but ... the LM_Set_Origin.lua tool ... contains:

Code: Select all

-- register the layer window to be updated when changes are made
table.insert(MOHO.UpdateTable, LM_LayerSettingsDialog_Update)
Which actually triggers the dialog to update during updates, and it passes moho...?

Code: Select all

function LM_LayerSettingsDialog_Update(moho)
Now that is interesting -- the table MOHO.UpdateTable and the function MOHO.RunUpdateTable have been there since at least 12.5 -- I haven't dug deeper into the past. The function is described briefly in http://mohoscripting.com/lm_utilities

This is the code...

Code: Select all

MOHO.UpdateTable = {}

function MOHO.RunUpdateTable(moho)
	for i, func in ipairs(MOHO.UpdateTable) do
		func(moho)
	end
end
But it does indeed suggest that the MOHO.UpdateTable is a list of functions to be run "when changes are made" and that they are passed an instance of moho -- although right now I don't know what exactly will trigger the MOHO.RunUpdateTable function to run, and when an entry is removed from the table.
User avatar
hayasidist
Posts: 3565
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Let's suggest new API features

Post by hayasidist »

I've finally done some more on this ...
It's dead easy to put a routine in the table and it does get run when the user clicks on a new tool or moves the timeline (and, I guess, similar events).

Code: Select all

function HS_ForUpdate(moho)

	local t = type (moho.frame)

	Print ("I'm here!!", t)
end
--
	table.insert(MOHO.UpdateTable, HS_ForUpdate)

However, the "moho" that the called routine gets doesn't seem to be the same as I was expecting - the above reports t as "nil" ... maybe something wrong with the way I'm setting things up -- but right now I'm at a bit of a brick wall.
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

hayasidist wrote: Thu Aug 05, 2021 10:10 amHowever, the "moho" that the called routine gets doesn't seem to be the same as I was expecting - the above reports t as "nil" ... maybe something wrong with the way I'm setting things up -- but right now I'm at a bit of a brick wall.
I've also been dabbling and it's all very confusing...

I also touched that brick wall a lot, and moho.layer == nil fires both true and false at the same time. (Obviously not at the same time, but the function fires twice I guess)

I did get something to work which seemed impossible before. And I imagine it's possible to maybe not have the window be local in a tool, but global in a utility? (I don't even know what I'm talking about, but hopefully one of you does) 😅

Save this as a tool:

Code: Select all

-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "LK_Experiment"

-- **************************************************
-- General information about this script
-- **************************************************

LK_Experiment = {}

LK_Experiment.BASE_STR = 2325

function LK_Experiment:Name()
	return "LK_Experiment...Name"
end

function LK_Experiment:Version()
	return "0.1"
end

function LK_Experiment:Description()
	return "LK_Experiment...Description"
end

function LK_Experiment:Creator()
	return "Lukas Krepel, Frame Order"
end

function LK_Experiment:UILabel()
	return "LK_Experiment...UILabel"
end

function LK_Experiment:ColorizeIcon()
	return true
end

-- **************************************************
-- Recurring values
-- **************************************************

LK_Experiment.matrix = LM.Matrix:new_local()
LK_Experiment.experimentalWindow = nil

-- **************************************************
-- The guts of this script
-- **************************************************

function LK_Experiment:OnInputDeviceEvent(moho, deviceEvent)
	--
end

function LK_Experiment:OnMouseDown(moho, mouseEvent)
	--
end

function LK_Experiment:OnMouseMoved(moho, mouseEvent)
	--
end

-- **************************************************
-- Layer Settings dialog
-- **************************************************

local LK_ExperimentDialog = {}

LK_ExperimentDialog.CHANGE = MOHO.MSG_BASE
LK_ExperimentDialog.XPOS = MOHO.MSG_BASE + 1

function LK_ExperimentDialog:new()
	local d = LM.GUI.SimpleDialog("Experimental Window", LK_ExperimentDialog)
	local l = d:GetLayout()

	l:PushH()
		d.frame = LM.GUI.DynamicText("Frame 00000", 80)
		l:AddChild(d.frame, LM.GUI.ALIGN_LEFT)
		-- * LM.GUI.Slider(length, vertical, showTicks, msg, resizingMode)
		d.slider = LM.GUI.Slider(200, false, false, LK_ExperimentDialog.CHANGE, LM_FOLLOW_LEFT)
		l:AddChild(d.slider)
	l:Pop()
	l:PushH()
		l:AddChild(LM.GUI.StaticText("Layer X translation:"))
		d.pX = LM.GUI.TextControl(0, "00.0000", LK_ExperimentDialog.XPOS, LM.GUI.FIELD_FLOAT)
		l:AddChild(d.pX, LM.GUI.ALIGN_LEFT)
	l:Pop()
	return d
end


function LK_ExperimentDialog:Update(moho)
	if (moho.layer == nil) then
		-- print ("moho.layer == nil (?!)")
		return
	end
	-- print ("moho.layer ~= nil (WHAT?)")

	self.frame:SetValue("Frame "..moho.frame)
	self.slider:SetRange(moho.document:StartFrame(), moho.document:EndFrame())
	self.slider:SetValue(moho.frame)

	local layer = moho.layer
	self.pX:SetValue(layer.fTranslation.value.x)
end

function LK_ExperimentDialog:OnOK()
	LK_Experiment.experimentalWindow = nil -- mark the window closed
end

function LK_ExperimentDialog_Update(moho)
	if (LK_Experiment.experimentalWindow) then
		LK_Experiment.experimentalWindow:Update(moho)
	end
end

-- register the layer window to be updated when changes are made
table.insert(MOHO.UpdateTable, LK_ExperimentDialog_Update)

-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************

LK_Experiment.CHANGE = MOHO.MSG_BASE
LK_Experiment.XPOS = MOHO.MSG_BASE + 1

function LK_Experiment:DoLayout(moho, layout)
	if (self.experimentalWindow == nil) then
		self.experimentalWindow = LK_ExperimentDialog:new()
		self.experimentalWindow:DoModeless()
	end
end

function LK_Experiment:UpdateWidgets(moho)
	if (self.experimentalWindow) then
		self.experimentalWindow:Update(moho)
	end
end

function LK_Experiment:HandleMessage(moho, view, msg)
	if (msg == self.CHANGE) then
		-- * Slide to new frame:
		local newFrame = LK_Experiment.experimentalWindow.slider:Value()
		moho:SetCurFrame(newFrame)
	end
	if (msg == self.XPOS) then
		-- * Prep undo:
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		-- * Key layer translation:
		local layerPos = moho.layer.fTranslation:GetValue(moho.frame)
		layerPos:Set (LK_Experiment.experimentalWindow.pX:FloatValue(), layerPos.y, layerPos.z)
		moho.layer.fTranslation:SetValue(moho.frame, layerPos)
		-- * Update timeline:
		moho:UpdateUI()
	end
end
It will popup a dialog that is able to get and set the current frame of the timeline. And also get and set the translation of the current layer.

If you select a different tool, the dialog becomes silent, until you reselect the tool again.

But... If you add handle message and update widget functions to the ones of another tool, that tool is also able to keep the window active!

Code: Select all

function LM_SomeOtherTool:HandleMessage(moho, view, msg)
	LK_Experiment:HandleMessage(moho, view, msg)

Code: Select all

function LM_SomeOtherTool:UpdateWidgets(moho)
	if (LK_Experiment.experimentalWindow) then
		LK_Experiment.experimentalWindow:Update(moho)
	end
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

Okay, here is a simpler version...

Add this as a button or menuscript:

Code: Select all

-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "LK_Experiment"

-- **************************************************
-- General information about this script
-- **************************************************

LK_Experiment = {}

function LK_Experiment:Name()
	return "LK_Experiment...Name"
end

function LK_Experiment:Version()
	return "0.1"
end

function LK_Experiment:Description()
	return "LK_Experiment...Description"
end

function LK_Experiment:Creator()
	return "Lukas Krepel, Frame Order"
end

function LK_Experiment:UILabel()
	return "LK_Experiment...UILabel"
end

function LK_Experiment:ColorizeIcon()
	return true
end

-- **************************************************
-- Layer Settings dialog
-- **************************************************

local LK_ExperimentDialog = {}

function LK_ExperimentDialog:new()
	local d = LM.GUI.SimpleDialog("Experimental Window", LK_ExperimentDialog)
	local l = d:GetLayout()

	l:PushH()
		d.frame = LM.GUI.DynamicText("Frame 00000", 80)
		l:AddChild(d.frame, LM.GUI.ALIGN_LEFT)
		d.slider = LM.GUI.Slider(200, false, false, LK_Experiment.CHANGE, LM_FOLLOW_LEFT) -- * LM.GUI.Slider(length, vertical, showTicks, msg, resizingMode)
		l:AddChild(d.slider)
	l:Pop()
	l:PushH()
		l:AddChild(LM.GUI.StaticText("Layer X translation:"))
		d.pX = LM.GUI.TextControl(0, "00.0000", LK_Experiment.XPOS, LM.GUI.FIELD_FLOAT)
		l:AddChild(d.pX, LM.GUI.ALIGN_LEFT)
	l:Pop()
	return d
end


function LK_ExperimentDialog:Update(moho)
	if (moho.layer == nil) then
		-- print ("moho.layer == nil (?!)")
		return
	end
	-- print ("moho.layer ~= nil (WHAT?)")

	self.frame:SetValue("Frame "..moho.frame)
	self.slider:SetRange(moho.document:StartFrame(), moho.document:EndFrame())
	self.slider:SetValue(moho.frame)

	local layer = moho.layer
	self.pX:SetValue(layer.fTranslation.value.x)
end

function LK_ExperimentDialog:OnOK()
	experimentalWindow = nil -- mark the window closed
end

function LK_ExperimentDialog_Update(moho)
	if (experimentalWindow) then
		experimentalWindow:Update(moho)
	end
end

-- register the layer window to be updated when changes are made
table.insert(MOHO.UpdateTable, LK_ExperimentDialog_Update)

-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************

LK_Experiment.MSG_BASE = 	2000 -- * Might conflict with other tools!
LK_Experiment.CHANGE =		MOHO.MSG_BASE + LK_Experiment.MSG_BASE + 0
LK_Experiment.XPOS =		MOHO.MSG_BASE + LK_Experiment.MSG_BASE + 1

function LK_Experiment:Run(moho)
	if (experimentalWindow == nil) then
		experimentalWindow = LK_ExperimentDialog:new()
		experimentalWindow:DoModeless()
	end
end

function LK_Experiment:HandleMessage(moho, view, msg)
	-- print ("msg "..msg)
	if (msg == self.CHANGE) then
		-- * Slide to new frame:
		local newFrame = experimentalWindow.slider:Value()
		moho:SetCurFrame(newFrame)
	end
	if (msg == self.XPOS) then
		-- * Prep undo:
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		-- * Key layer translation:
		local layerPos = moho.layer.fTranslation:GetValue(moho.frame)
		layerPos:Set (experimentalWindow.pX:FloatValue(), layerPos.y, layerPos.z)
		moho.layer.fTranslation:SetValue(moho.frame, layerPos)
		-- * Update timeline:
		moho:UpdateUI()
	end
end
And add this to all other tools:

Code: Select all

function LM_SomeOtherTool:HandleMessage(moho, view, msg)
	function LM_AddPoint:HandleMessage(moho, view, msg)
	LK_Experiment:HandleMessage(moho, view, msg)
	if msg >= MOHO.MSG_BASE + LK_Experiment.MSG_BASE then -- * Might conflict with the original tools intentions!
		return
	end

Code: Select all

function LM_SomeOtherTool:UpdateWidgets(moho)
	LK_ExperimentDialog_Update(moho)
Because handlemessage is forwarding the msg, I've added a msg_base value to the script. But I'm not exactly sure if this is a terrible idea:

Code: Select all

LK_Experiment.MSG_BASE = 	2000 -- * Might conflict with other tools!
LK_Experiment.CHANGE =		MOHO.MSG_BASE + LK_Experiment.MSG_BASE + 0
LK_Experiment.XPOS =		MOHO.MSG_BASE + LK_Experiment.MSG_BASE + 1
I can imagine a modal window that simply controls smartbonedials. Or that script I once wrote (I'm sure it's somewhere on this forum) that toggles bones by color. It would be nice to be able to just have it open and use it with any tool.
Last edited by Lukas on Thu Aug 05, 2021 6:56 pm, edited 2 times in total.
User avatar
synthsin75
Posts: 10013
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Let's suggest new API features

Post by synthsin75 »

The experimental menu script just throws an error from the AddPoint tool. Likely due to that MSG_BASE.
User avatar
Lukas
Posts: 1300
Joined: Fri Apr 09, 2010 9:00 am
Location: Netherlands
Contact:

Re: Let's suggest new API features

Post by Lukas »

synthsin75 wrote: Thu Aug 05, 2021 5:37 pm The experimental menu script just throws an error from the AddPoint tool. Likely due to that MSG_BASE.
Ahh, yes, because the addpoint tool's handlemessage checks for "elseif (msg >= self.SELECTITEM) then" it will likely always trigger a part of that code which is not supposed to run and throw an error... I'm don't really understand how MSG_BASE works and what its min/max values are. There's probably no sure way to make sure the msg integers of the modal window won't overlap with the tools without modifying some of them.

This works, but is also probably problematic:

Code: Select all

function LM_AddPoint:HandleMessage(moho, view, msg)
	LK_Experiment:HandleMessage(moho, view, msg)
	if msg >= MOHO.MSG_BASE + LK_Experiment.MSG_BASE then
		return
	end
	[...]
User avatar
hayasidist
Posts: 3565
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Let's suggest new API features

Post by hayasidist »

Lukas wrote: Sat Apr 10, 2021 10:43 pm Check out the LM_Set_Origin.lua tool ... the tool also contains:

Code: Select all

-- register the layer window to be updated when changes are made
table.insert(MOHO.UpdateTable, LM_LayerSettingsDialog_Update)
has anyone found / got access to the code for the LM_LayerSettingsDialog_Update function? How that handles the "moho" param would be very illuminating!

I've not found it in the "Resources" directory from the installation so I'm guessing it isn't part of any LUA tool script to which we should have access.
Post Reply