Open ThistleSifter opened 2 years ago
My vote would be for a full wrapper of FCCustomLuaWindow
with support for modeless and finenv.RetainLuaState
. The requirements would be:
State includes:
x
, y
coordinates.If this is the first time running the script (i.e., finenv.RetainLuaState
is false
), the wrapper builds the window as it would now and builds the state alongside it.
If this is not the first time running the script (i.e., finenv.RetainLuaState
is true
), the wrapper builds the window using the state variables to initialize the controls and then moves the window to the saved x
, y
coordinates.
When the window closes and/or the script is ending, the wrapper captures the value of each control as well as the x
, y
coordinates.
The simplest script I know of that is doing this (without a wrapper of course) is transpose_by_step.lua
.
Part of what you're after, as far as controls etc goes, is already happening. Run the following script in RGPLua and type things in the edit, then close and run it again.
local mixin = require("library.mixin")
if not finenv.RetainLuaState or not dialog then
dialog = mixin.FCXCustomLuaWindow()
dialog:CreateEdit(10, 10):SetText('hi')
finenv.RegisterModelessDialog(dialog)
finenv.RetainLuaState = true
end
dialog:ShowModeless()
That just leaves the window's position and size, which I'll look into.
I haven't done anything with size, only with position. (Mine being dialog boxes with fixed control positions.) But I think the size functions are hooked up in the latest RGP Lua along with the position functions.
Also, I love this example. I think it should probably be in the docs somewhere. (If it already is, my apologies. I've had my head buried in other project lately, but I mean to get back around to this.)
Thanks for the reminder about the docs, I've been meaning to update them but haven't gotten around to it yet.
Anyway, it turns out that the example above is somewhat of a fluke (the contents of an edit are only stored over if the text is manually set before the first time the window is shown) and the behaviour becomes even weirder for other controls like popups.
Is there anything you can do about it within RGP Lua? If not, I'll look into a solution in the mixins.
@ThistleSifter As we discussed over on the PR thread, the PDK Framework is not designed with the ability to reuse FCCustomDialog
or FCControl
instances to open a 2nd or subsequent dialog after the first. It may work in some cases, but there are no guarantees. I'm having some success at the moment, but I realized something today that is possibly a bump in the road.
@Nick-Mazuk the mixin libraries for individual classes are not explicitly required
in the code. Rather, the mixin library figures out which ones are needed and requires them by programmatically constructing their names and passing them to the require
function. This works great, but it throws a wrench into our stand-alone deployment strategy. A simple scan of the source code does not reveal the library dependencies. If we are going to adopt mixins in a big way, we may need to come up with a different deployment strategy.
To give an example, the follow line of code kicks off a chain reaction of programmatically constructed require
statements:
local window = mixin.FCXCustomLuaWindow()
The mixin library recursively attempts to require
mixin class scripts all the way down the inheritance chain to __FCMBase
. It's very cool, but it's also very hostile to stand-alone deployment. I'm not sure how to proceed.
Hmm… to get a single-file deployment, we need to "undo" all the require statements.
For background with how it currently works, let's say you have the following library/foo.lua
:
local foo = {}
foo.bar = function()
return "hello world"
end
return foo
And now you have the following script my_script.lua
:
local foo = require("library.foo")
print(foo.bar())
This currently will get bundled as the following bundled.lua
, where essentially the contents of library/foo.lua
will replace the line local foo = require("library.foo")
:
let foo = {}
foo.bar = function()
return "hello world"
end
print(foo.bar())
So if we want to let people still download the script as a single file (preferred), we'll need to find a way to merge all the scripts into a single file.
Since the mixing imports seem to be dynamic, would it be possible to instead just bundle the entire mixin code into every file? That way we don't need to analyze any runtime require statements? We can just look to see if the mixins are imported. And if so, we bundle in all the mixins.
Do you foresee any issues with this approach?
Dumping in all the mixin files is the best approach I can think of myself, though it will potentially create absurdly long deployed scripts. I have never seen any performance hit related to long scripts, but it is something to keep in mind as we go down the path.
I keep circling around to thinking about luac
as a way to make the scripts more of a black box experience for end users. Unfortunately, there is no simple way that I can figure out to support the plugindef
function if it is in a compiled script file. Which ends up, at least so far as I can tell, leaving luac
off the table.
I would like to convert my scripts with dialog boxes to use FCXCustomLuaWindow
, and that will be an opportunity to see how well it all works in real life.
Yeah, file sizes could get quite large. Luckily, I found a library to minify the source code:
https://github.com/mathiasbynens/luamin
It'll still keep an absurd amount of unused code, but the file size should still be smaller.
I think I'll also experiment if a dedicated Lua bundler will work (e.g., https://github.com/Benjamin-Dobell/luabundle). Perhaps it'll be smart enough to only bundle the necessary files? From looking at the mixin code, I don't think it'll do that, but perhaps it might.
I just remembered this comment https://github.com/finale-lua/lua-scripts/pull/144#issuecomment-1069101364 and I was wondering if it was suggesting the possibility of bundling mixins with RGPLua?
I've never played around with luac
so I don't know how it works, but I just had this passing crazy idea. I'm assuming that even though it's compiled, the debug library still works. And from that, I was wondering if the concept of a line still exists at some level, since a number of the functions in the debug library deal with lines. So to extract plugindef
, I was wondering if you could execute it in a sandbox of sorts, adding a hook for line change and just wait until either the plugindef
function has been registered or the finale
namespace is accessed, at which point the script is terminated.
Edit: Still with the sandbox idea, you could replace the finale
namespace with a dummy object and when it's accessed for the first time, check if there's a plugindef
function and if there is, call it and do whatever processing needs doing. If by then there is no plugindef
defined, either it doesn't exist or the author didn't follow the guidelines. And then you just terminate.
Just chiming into say that the bundler now handles mixins properly and they can be used in future code.
I was wondering if you could execute it in a sandbox of sorts, adding a hook for line change and just wait until either the plugindef function has been registered
That's actually quite an interesting idea. One reason it could be trouble (assuming it actually works) is if the script does something before registering the plugindef
function. Right now, RGP Lua (and also I speculate, JW Lua) does not care where in the script you put plugindef
. But for this approach to be safe, the script would have to put it first.
Now that I've added most of the mixins that I had already planned, I just thought I'd post an issue to discuss what to do next with the mixin library. Here are some brief ideas that I had:
FCString
in controls and dialog classes, but I could go through the rest of the PDK.