Closed macMikey closed 4 years ago
I don't think that it is necessary to do so. Originally SOS stacks didn't allow you to define the parent behavior so there was no way to have a parent behavior assigned to an SOS stack unless you assigned it at run-time. Now that the engine support the with behavior
syntax for SOS stacks this isn't needed.
LoadBehavior
was added prior to with behavior
being available and was a workaround of sorts. It may still have some value but I haven't set aside time to think through it.
The underlying issue that still needs to be addressed is that when a stack loads that has a behavior assigned to it (or if any controls in the stack have behaviors assigned to them), the behavior will not be associated with the stack (or control) if the behavior object is not loaded in memory.
Example scenario where the behavior will not be associated with the stack:
stackFiles
property of Stack B (or any other stack already in memory).When Stack B opens Stack A is not available and cannot be found by the engine. Thus the behavior assignment fails and your code doesn't work as expected.
Unfortunately the engine doesn't send any notifications when a behavior fails to load. It just fails silently.
As I write this, it occurs to me that LoadBehavior
can probably be done away with if all stacks in the behaviors
folder are added to the stackFiles
property of the app
stack. All UI stacks are added as stackFiles
which means you can always reference the stack by name and the engine will know where to find it. The same thing could be done with behaviors
so the engine will always know where to find the behavior stack when needed.
The stackfiles
idea is fine until the stack ceases to be, then instead of the dreaded silent fail you get a bizarre message from the debugger
Maybe this is the same fail that you were seeing. In this case it occurred because there was a behavior in the stackfiles
that had been removed from the project, and there was still a reference to it in a behavior chain.
In the short term, I don't think dropping the existing syntax is a good idea. Navigator
still generates behaviors the old way and then outputs a list of script lines to be added to a stack to ensure that the new SOS's get their behaviors assigned. That said, I'm going to issue an issue with GC to update it.
Unfortunately, Levure is going to have to consider its place as the de facto LC framework, and therefore be considerate of the unwashed masses coming for it, as itty bitty as they may be.
although, you know what? i'm also the only person who even noticed this, so maybe i'm thinking about something that doesn't even matter, and the plan should be to tell everyone to use the with behavior
syntax.
one other thing that i just discovered - with behavior
only allows you to reference other SOS's. You can't reference buttons for instance, so for legacy apps that are using references to button behaviors (e.g. apps using tmc2, or some dg's, because i think there are some button script behaviors), with behavior
won't help.
The stackFiles
property of the Levure app
stack is always accurate as it is built when the application is loaded. So in this case we can leverage the stackFiles
for the stacks in the root behaviors
folder without worrying that it will get out of date.
I'm not familiar with Navigator but it sounds like you are working with UI stacks and not stacks that would go in the behaviors
folder. It sounds like code is generated that would go in the preOpenStack
handler. Am I correct?
Can you provide an example where a legacy application would need to store a script only stack in the root behaviors
folder and would need to reference a control within a stack as a behavior? Ideally Levure should never add messages that aren't really necessary. Sending loadBehavior
to stacks in the root behaviors
folder was a workaround for an engine limitation that has since been lifted. In addition, if Levure starts assigning those stacks to the app
stack stackFiles
property as described previously then the "behavior object isn't loaded into memory so fails to be assigned as a behavior" issue goes away as well.
Recall that with behavior
can only reference stacks. It cannot reference buttons.
The problem in this case is accommodating a legacy product and a legacy technique, namely behaviors that are in buttons in another stack.
The example that immediately comes to mind is tmControls.
Remember that the use case is extracting scripts from existing objects and putting them into SOS's.
The interface objects are groups that contain a number of objects, like fields, graphics elements (like backgrounds and foregrounds) and icons. The script for each object resides in the group, and thus would be extracted into a ui behavior. Below is the "Settings" button, exploded, in the PB.
Below you can see the chain. The first behavior is the script that was previously in the group, but is now a separate UI behavior
The second behavior is a button script for this type of object (a pushbutton)
The third behavior is also a button script, the master behavior
Each of the object types gets its behavior from a button in a separate stack. Each of those buttons inherits the "master behavior" from another stack. There is a third separate stack that has the behavior and design elements for the table object.
Below is the exploded PB for those two stacks.
Each UI stack has its own separate tmc behaviors, uniquely named, as you would expect, so that LC is not confused by the names being the same, and so that unloading one UI stack does not cause the others to lose their behaviors.
What I did to make this work was:
/app/behaviors
folder to ensure that they are in memory when the ui loadspreopenstack
handler for the ui stacks, walk the ui stack's stacksinuse and assign each of the controls' behavior buttons. I actually ended up writing loadBehavior
routines for each of those stacks and then invoking them.It sounds like the stacks you add to the ./app/behaviors
folder do not need to have any modifications made in a LoadBehavior
routine. They are placed there so that the are automatically loaded into memory when the app loads. Correct?
Is it correct that you converted your group scripts (e.g. the Settings
group script) to SOS and the behavior of each group SOS needs to point to a tmc behaviors which is a button? It sounds like it is only your SOSs that need to have their behaviors assigned when the UI stack loads.
To me it sounds like your situation is best handled in the preOpenStack of the UI stack, like you are doing now. You are leveraging the ./app/behaviors
folder to make sure a global behavior is always available and then coding a workaround necessary for using SOSs with parent behaviors that are buttons.
I would prefer to do away with Levure sending a LoadBehavior
message entirely as I don't think it is a message that the framework should be synthesizing. I feel that the engine should be sending a message when a stack is loaded into memory. I also do not want to extend it to the behaviors associated with UI stacks. Levure doesn't load UI stacks into memory when the app launches. This improves memory usage and allows a developer to manage which UI stacks stay in memory using destroyStack
. To add a LoadBehavior
message for UI stacks Levure would have to monitor when a UI stack was being loaded into memory and then send the LoadBehavior
message to the behaviors at that time.
I don't think that's a bad decision. I think that grasping the Tao of Levure is something that adopters have to do, and part of the Tao is "Less".
👍🏻
Would it be beneficial to send loadBehavior to ui behavior stacks, too? Example - any project that uses theming from tmc2 has behaviors assigned to every object. That chain is typically two or three deep. The stack also has a chained master behavior assigned to it.