mastodon-sc / mastodon

Mastodon – a large-scale tracking and track-editing framework for large, multi-view images.
BSD 2-Clause "Simplified" License
66 stars 20 forks source link

Rework the application model #253

Closed tinevez closed 8 months ago

tinevez commented 10 months ago

This PR relates to a large amount of changes focused on reworking the application model. That is: the classes and logic that control the end-user application specific to the Mamut application (Spot and Link on a ModelGraph). It contains major API breaking changes which will prompt for a new beta version, hopefully the last one. This PR serves as a holder for the description of the changes as they are committed. Ideally a documentation will be extracted from the text appended here.

These changes try to address old and rather difficult TODOs that sometime appeared rather early in the project. For instance: In the beginning Mastodon had only two views and everything could be hardcoded. But now there are 7 of them, and new ones are coming, yielding a very long window manager class with many similar hard coded methods and many fields. The plugin system was built along the way while the app model was being elaborated. Loading and saving were handled by a component of the window manager (the ProjectManager), that could set, change or nullify the app model within one window manager instance, and dependents of the app model had to deal individually with these possibilities. And Mastodon had a lot of UI-related classes (the Keymaps, CommandDescriptions, SettingsPage frameworks) that Tobi moved recently to upstream packages (BDV-core and ui-behaviour).

This PR tries to address them, adding or tweaking features along the way.

Almost all the changes are invisible to the user. There are no changes in the file format. But developers can expect a lot of compile errors when they will depend on this PR.

The new ProjectModel class.

The MamutAppModel class have been renamed as ProjectModel and is now the central class that manages everything related to one 'session'. That is: when the user loads or creates a project, a new ProjectModel instance is created that has everything needed.

The plugins receive directly a ProjectModel instance.

There is no need to make a separate class for the model provided to the plugins. They receive the ProjectModel, and stressing again: which is never null and never changes. So there is no need to check whether its null or to update the commands the plugin provides.

The interface for Mamut plugins is now:

public interface MamutPlugin extends MastodonPlugin< ProjectModel >
{}

Loading and saving.

The ProjectManager class is gone, replaced by 4 classes with only static methods, in the package org.mastodon.mamut.io. Each one takes care about loading, saving, importing or exporting.

The loading methods all produces a new ProjectModel instance. This instance receives the MamutProject it was created on (handy to save it later). The project model is properly initiatialized ONCE with everything. Then a consumer can create a MainWindow to pilot it. Example:

final ProjectModel appModel = ProjectLoader.open( projectPath, new Context() );
final MainWindow win = new MainWindow( appModel );
win.setVisible( true );

The MamutProject and related classes have also been moved. MamutProjectIO has now only static methods.

Generic views for Mastodon in the window manager.

The view (MamutViewBDV, MamutViewTrackScheme, ...) aer now created each by a specific factory, implementing the interface org.mastodon.mamut.views.MamutViewFactory. It is a discoverable SciJavaPlugin, that defines how to create a specific view using a ProjectModel instance, and how to de/serialize its GUI state.

The window manager has now a MamutViews field that is in charge of detecting and managing a collection of these view factories. Each factory is then used by the window manager to make one action that creates a view, registers it in the WM, deals whether it has a context provider or a context chooser, notifies listeners of its creation (see below) and finally shows it.

To create a specific view, you simply have to provide its class. For instance to create and show a BDV view:

ProjectModel projectModel = ...
final WindowManager wm = projectModel.getWindowManager();
MamutViewBdv view = wm.createView( MamutViewBdv.class );

the new view will be properly registered and its context provider will be made available to all other context chooser views.

New views are created, registered and managed only via these factories now. Which means that that window manager does not know of any concrete view class. It just manages all the ones it found. This will make it easier to extends Mastodon with new views, without having to touch or even recompile the core.

This also makes the window manager class much shorter. There are much less methods and much less fields, since all views are managed in a generic manner.

The view factory are also nice because they contain in one place all the info related to an action: command name, menu text, description.

For ELEPHANT, Ko requires that there is a view creation listeners list for BDV window. I kept this, but made it generic. There is now one listener list per view type. So you can register a listener for the creation of any type of view, and get it before it is shown.

The factories also handle GUI state serialization. The ProjectLoader and ProjectSaver classes use these factories when loading / saving for restoring / saving the GUI state. There are no specific hard-coded code to handle serialization of GUI state in the io package. This de/serialization also now takes much less code than before, as there were a lot of duplicated code, which is now present once in the mother abstract class for view factories. Also: the views are now decoupled from saving or restoring the GUI state. The factory does it, which is cleaner.

All the view classes have been moved to subpackages, organized as follow: Screenshot 2023-08-25 at 10 36 42

Style managers and Settings pages are also managed by a factory pattern.

The various managers (RenderSettingsManager, ...) are managed in a similar way in the window manager. There is a factory interface for each (org.mastodon.mamut.managers.StyleManagerFactory), and it can return whether the style manager has a Settings page. If it is the case, it is automatically added to the PreferencesDialog.

Replace KeyMap and associated classes by upstream implementations.

The Keymap, KeymapManager, CommandDescriptions, SettingsPage and all associated classes have been migrated in upstream repos, mainly the BDV-core and ui-behaviour. Their initial implementation have been removed from Mastodon, which now use the common ones.

There are two adaptations specific for Mastodon:

1/ The KeymapManager class is extended by a specific one for Mastodon so that we can load and save keymaps in the Mastodon folder, and so that we load the builtin keymaps we had before.

2/ The new CommandDescription requires specifying a context AND now a scope. I made two scopes: one for the actions that are generic to Mastodon (everything in views, that do not know of the Mamut model), and one specific for the Mamut application (everything under org.mastodon.mamut, which is the one users have).

I could not replace one class:

Better error messages and failing gracefully when opening projects.

I also worked on having the launcher not hang when something wrong happens. It should now show a meaningful error message to the user and allows them to open another project. Screenshot 2023-08-25 at 10 38 05

The views and dialogs show the current project name.

And it is updated when the user saves the project under a different name.

Screenshot 2023-08-25 at 10 30 35