SonyWWS / ATF

Authoring Tools Framework (ATF) is a set of C#/.NET components for making tools on Windows. ATF has been in continuous development in Sony Computer Entertainment's (SCE) Worldwide Studios central tools group since early 2005. ATF has been used by most SCE first party studios to make many custom tools such as Naughty Dog’s level editor and shader editor for The Last of Us, Guerrilla Games’ sequence editor for Killzone games (including the Killzone: Shadow Fall PS4 launch title), an animation blending tool at Santa Monica Studio, a level editor at Bend Studio, a visual state machine editor for Quantic Dream, sound editing tools, and many others.
Apache License 2.0
1.89k stars 262 forks source link

GlobalHistoryContext with multiple EditingContext on same DomNode #49

Closed avenger3pt0 closed 8 years ago

avenger3pt0 commented 8 years ago

I'm currently running into an issue collapsing all of the HistoryContext for our tool into one GlobalHistoryContext. Our Schema loading looks like this:

Schema.parentNodeType.Type.Define(new ExtensionInfo<GlobalHistoryContext>());

Schema.childNodeType.Type.Define(new ExtensionInfo<EditingContext1>());
Schema.childNodeType.Type.Define(new ExtensionInfo<EditingContext2>());

The problem is that during GlobalHistoryContext.AddNode(DomNode node) the child HistoryContext get put into a HashSet<HistoryContext>. This means that m_childHistoryContexts only contains EditingContext1.

If EditingContext2 happens to be the active context when the user tries to Undo certain operations in our tool, a new Transaction is added to the CommandHistory in HistoryContext.OnEnded() due to m_undoingOrRedoing not being set to true during GlobalHistoryContext.SynchronizeUndoRedoStatus(bool newStatus).

I tried to derive from GlobalHistoryContext to implement our own version that can handle multiple EditingContext on a single DomNode, but that does not work because the set for HistoryContext.UndoingOrRedoing is set to internal access level.

Do we need to change the structure of our schema or is this something that can be supported in ATF?

abeckus commented 8 years ago

I will look into this issue as soon as I get a chance. Though, I think a simpler approach is to use one EditingContext and registered it on parentNode instead of having multiple history contexts.

Schema.parentNodeType.Type.Define(new ExtensionInfo()); Then make EditingContext to work on the entire tree.

For another way to solve the problem look at GameContext.cs in LevelEditor project GameContext.cs deals with multiple history contexts. Because each sublevel is a separate document so it needs to have its own History Context. But if the entire DomTree in project is stored in one document then one history context at the root node is sufficient and simpler.

Thanks Alan

avenger3pt0 commented 8 years ago

Thanks for the quick reply! I will take a look at what LevelEditor does and see if that can be adapted.

The reason we have multiple EditingContext on that one node, is that they are ITreeView editors for different sets of data on the childNodeType (we use the DomNode property as the Root of the tree and return specific children from the childNodeType in GetChildren()). Is there a better way to implement that type of behavior?

abeckus commented 8 years ago

You are welcome if each set of data needs to have its own history context (undo/redo stack) then your are dong the right thing to use GlobalHistoryContext. (Currently I am busy but I will take look to make required changes to work for you case). But if your use case allows to have one undo/redo stack for all the data sets. The root node can listen to any changes from any descendent nodes. All the DomNode events bubble up to the root node you can hook subscriber at any level up to the root node.

Regarding LevelEditor's approach. It might not work for you because it address different issue. The main Level (Root Node) can have N number of sublevels each of them is separate document stored in its own file. But all of them are rendered into one viewport. Each sublevel has its own history context but they are synchronized to have same undo depth.

Alan

avenger3pt0 commented 8 years ago

You are correct that the LevelEditor's approach is not what we are looking for.

Any idea on what your solution will look like for modifying GlobalHistoryContext?

abeckus commented 8 years ago

I pushed some changes to ATF from our internal main branch. Sync and look at the release note located at root folder for detail.

Regarding multi history contexts, I suggest not to use GlobalHistoryContext instead implement your own multi-history context without derive from the global one. In your case you need to register your MultiHistoryContext on parentNode. and register EditingContext on each child node. When user activates an editor that is bound particular child then set child's editing context to be active context. You can do this by using ContextRegistry.ActiveContext. In this case each editor will have its own undo/redo history. You need to tackle few issues to make it work. For example the logic that determines if the document is dirty needs to aggregate dirty flags for all the sub-contexts. you can do using your MultiHistoryContext. Note that dealing with multiple contexts is challenging for experienced ATF programmer. So it might take time to get it right.

Good luck Alan

avenger3pt0 commented 8 years ago

Sorry for the delay in replying, but I was out on a business trip.

Looking at your change, I'm not sure if making GlobalHistoryContext.SynchronizeUndoRedoStatus public virtual will help me implement MultiHistoryContext correctly. The member that I was having issues with was HistoryContext.UndoingOrRedoing. If I can not set that in my own history context, Transactions will still be added during undo/redo.

Is it possible to make HistoryContext.UndoingOrRedoing more accessible?

abeckus commented 8 years ago

Yes, for now make a local change. I will change the setter to public for next update.