-
Notifications
You must be signed in to change notification settings - Fork 0
SplitView Proposed Architecture
Refactoring Brackets to Support Split View with Multiple Documents requires quite a bit of hacking on the plumbing but there are only a few places that need to be heavily refactored to do so. To break this work up in to smaller pieces, an accompanying document SplitView Architecture Tasking has been created.
This proposal calls on EditorManager to manage all viewable files and their supporting data. This includes Editor or Read Only Viewer Placement, Workingsets and Editor or Read Only Viewer Instances. DocumentManager is refactored somewhat to remove Workingset management -- although some legacy APIs, Events, Functions, Commands, etc..., will remain for some time to maintain backwards compatibility.
Those items that are needed to maintain backwards compatibility have been identified and documented in the SplitView Extension Migration Guide and are detailed in the section below on Deprecating Legacy APIs
This proposal is an overview of the system along with functional details and implementation changes needed to build the SplitView feature.
Reimplemented by moving current Implementation into Editor and invoking Editor.getFocusedEditor() for the Editor object of the focused pane.
Reimplemented by moving current impl into Editor and invoking Editor.getCurrentlyViewedPath for the Editor object of the focused pane.
Creates a Read Only viewer for any [image] file/URI.
Replaces EditorManager._showCustomViewer, current implementation of ._showCustomViewer is moved to Editor as createCustomViewerForFile and invoked from EditorManager.
The URI passed into this function will determine what kind of viewer to create. Initially only image files are supported but this can grow to include, preferences, extension managers and the like just by creating a plugin for the view factory that is needed to support this API.
Supporting this function is a object view factory to support creating views for a URI. If there is no viewer for the URI then the brackets interstitial page will be shown.
Reimplemented as EditorManager.getFocusedEditor().getCurrentFullEditor()
This API will change the layout to match rows and columns.
Update pane height/width. Not initially implemented.
The Implementation of these functions will move from DocumentManager to EditorManager. See the section at the bottom of this document for a list of deprecated Workingset APIs that need to be kept for backwards compatibility.
Extension Authors and 3rd party developers are encouraged to use other methods whenever possible to work with the set of open documents. See the SplitView Extension Migration Guide for more information.
Workingset APIs we be migrated from DocumentManager. Some of these will APIs will continue to exist in DocumentManager to maintain backwards compatibility. These have the same functionality as in previous versions of Brackets except they will take a paneId to identify which pane's Workingset to work on.
Most of the Workingset APIs will take one of these special constants for paneId in addition to valid paneIds:
------------------------+-----------------------------------------------------------------------------------------------------
Constant | Usage
------------------------+-----------------------------------------------------------------------------------------------------
ALL_PANES | Perform the operation on all panes (e.g. search for fullpath in all Workingsets)
FOCUSED_PANE | Perform the operation on the currently focused pane (can also use EditorManager.getFocusedPane())
------------------------+-----------------------------------------------------------------------------------------------------
Returns {paneId: paneId, index: index) or undefined if not found
EditorManager Events will add paneId to event data
The following list of commands will move from DocumentCommandHandlers along with their corresponding implementation into a new module -- FileCommandHandlers. It may be easier to leave some of the Document object specific code handling in DocumentCommandHandlers and wire it up to FileCommandHandlers when working with Files that have Document object but a cursory review indicated that this wasn't necessary. This is an implementation detail that will be decided when the work is actually done.
The initial implementation will be mostly handled by EditorManager but an EditorLayoutManager object may be created just to help handle the layout. We shouldn't need to build for advanced layout mechanics since we only need, at most, 2 panes. Support for arbitrary rows and columns can be built into the EditorLayoutManager flyweight at a later date.
Whether or not the initial implementation uses an EditorLayoutManger is an implementation detail that will be decided when the feature is implemented.
Layout Rules:
- Only 1 pane or 1 row and 2 columns or 2 rows and 1 column are initially supported
- Any Pane created by this API initially will show the Brackets logo interstitial screen until the corresponding
EditorManagerfor that pane has been loaded with a document or image. - When an editor pane is destroyed, all documents in the corresponding Workingset for that pane are moved to another pane's Workingset. Since there is only 2 panes in the initial implementation this is just a matter of collapsing them down to the remaining Pane's Workingset.
Creating a pane is rendered at runtime and inserted it into the DOM. The Editor Instance will generate the HTML when the EditorManager asks for it and insert it into the DOM in the appropriate place to ensure proper keyboard navigation. The generated HTML can either come from a template rendered with Mustache or simple jQuery insertion.
EditorManager will handle the PanelManager's resize event and create an EditorLayoutManager object to assist with the layout.
PanelManager computes the new size of #editor-holder in its the window.resize event handler and triggers an event to resize that EditorManager subscribes to. Currently PanelManager only computes the Height and passes that as data but it needs to also compute Width and pass that as well so that the EditorLayoutManager object can verify that the editor holder doesn't get too narrow to handle 2 editor instances. At the point that it is too narrow then it will need to start resizing other columns to get a decent layout. This algorithm becomes more complex with more columns and rows. For now it can be a matter of going to something like 50%. We may want to bump up the shell's minimum width as well to avoid getting too small.
height and width are expressed in percentages when affixing the CSS to the columns (e.g. width: 40%). Doing it in a percentage and only applying to all except the rightmost column and bottom most row will yield a fluid layout. The API will reject setting the width on the rightmost column. For the initial implementation we may just go with 50% splits all around without the ability to resize.
#Implementing Pane Management
EditorManager will manage a Workingset for each of its editor panes. This may be abstracted and delegated into a EditorPane object if implementation starts to get to messy but the API to get the Workingset will be on EditorManager to make the interface easier to use. Management of the Workingset will move from the DocumentManager into EditorManager the and the Workingset will no longer be a collection of Document objects. It will be a collection of file names.
Note: To abstract the Workingset's pane location, each editor pane is addressed by paneId rather than row,col. This is a change from the previous draft which had row, col addressable panes. Valid paneId values cannot be false, 0, null, undefined or "" so that they can be used in truthy tests.
The shortcut paneIds for Workingset APIs avoid having to maintain a reference to the pane in which a file belongs. It also allows us to create 1 API rather than 2 for All and Focused derivatives.
WorkingSetView objects are created when the event editorPaneCreated is handled. SideBarView will handle this event and create a WorkingSetView object which is bound to the Workingset created for the pane and passed in as event data.
#open-files-container is a container which contains one or more .working-set-container divs in the DOM. Several 3rd Party Extensions rely or use the #open-files-container div. The extensions which style the elements will continue to work.
To support images in split view, we will need to change the Workingset rules to allow for images in Workingsets. This means that callers of the new Workingset APIs will need to check to make sure they can operate on a file by getting its file type from the language manager or by checking its extension.
The Workingset will basically just be a list of files that may or may not have a Document object owned by the Document Manager. The deprecated API, DocumentManager.getWorkingSet(), will filter out any files that don't have an associated Document object.
This currently works by listening to contextmenu events on the #open_files_container. This will change to listen to contextmenu events on an .open_files_container and the WorkingSetView who attached to the .open_files_container will be maintain the paneId from the EventData it was passed during the create event so that callers (Extensions) will be able to determine which editor has focus when the menu is invoked.
This will also trigger a focus action on the DOM node causing the Editor to gain focus. The Default extension, CloseOthers, will be retooled to work on the Workingset for the editor pane associated with the WorkingSetView that manages it and ask that EditorManager instance for the Workingset.
DefaultMenus:
$("#open-files-container").on("contextmenu", function (e) {
working_set_cmenu.open(e);
});
Becomes:
$(".working-set-container").on("contextmenu", function (e) {
e.workingSetPaneId = this._paneId;
working_set_cmenu.open(e);
});-------------------------------------+--------------------------------------------------------
API | Calls...
-------------------------------------+--------------------------------------------------------
DocumentManager.getCurrentDocument() | EditorManager
| .getFocusedEditor()
| .getDocument();
-------------------------------------+--------------------------------------------------------
DocumentManager.getWorkingSet() | $.map(EditorManager.getWorkingSet(ALL_PANES, …),
| function(fullPath) {
| if (DocumentManager.isDocument(fullPath)) {
| return fullPath;
| }
| });
-------------------------------------+--------------------------------------------------------
DocumentManager.findInWorkingSet() | return EditorManager
*only used by pflynn | .findInWorkingSet(ALL_PANES, ...).index || -1;
-------------------------------------+--------------------------------------------------------
DocumentManager.addToWorkingSet() | return EditorManager
| .addToWorkingSet(FOCUSED_PANE, ...);
-------------------------------------+--------------------------------------------------------
DocumentManager.addListToWorkingSet | return EditorManager
| .addListToWorkingSet(FOCUSED_PANE, ...);
-------------------------------------+--------------------------------------------------------
DocumentManager.removeFromWorkingSet | return EditorManager
| .removeFromWorkingSet(ALL_PANES, ...);
-------------------------------------+--------------------------------------------------------
The following Events will be kept on the DocumentManager object to maintain backwards compatibility but will just be a repub of the EditorManager events.
Q: Is there a way to know if there are any listeners so that a deprecation warning can be written to the console only if there are listeners?
Fired when handling EditorManager.fullEditorChanged
The following EditorManager functions will move to Editor and are opportunistic refactorings since we're working in that code. These refactorings aid in the overall implementation but aren't necessary.
------------------------+-----------------------------------+-------------------------------------
Name | Usage | Disposition
------------------------+-----------------------------------+-------------------------------------
_openInlineWidget | Internal Inline Widget Management | Moves to Editor without impunity
------------------------+-----------------------------------+-------------------------------------
_toggleInlineWidget | Command Handler | Current Implementation moves to
| | Editor.
| | Command handler will call
| | getFocusedEditor()
| | .toggleInlineWidget()
------------------------+-----------------------------------+-------------------------------------
_showCustomViewer | API | Illegal usage from
| | DocumentCommandHandlers
| | Command handler moves to
| | FileCommandHandlers
| | Function is renamed to
| | EditorManager
| | createViewerForFile
------------------------+-----------------------------------+-------------------------------------
closeInlineWidget | | Moves to Editor
| | InlineWidget.close will call
| | this.hostEditor.closeInlineWidget()
------------------------+-----------------------------------+-------------------------------------
getInlineEditors | | Moves to Editor
| | InlineWidget
| | ._syncGutterWidths(
| | hostEditor
| | )
| | calls hostEditor.getInlineEditors()
------------------------+-----------------------------------+-------------------------------------
getFocusedInlineWidget | | Doesn't move but passes through to
| | getFocusedEditor()
| | .getFocusedInlineWidget();
------------------------+-----------------------------------+-------------------------------------
Raw data can be found here: splitview architecture notes