Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi Window support (Floating windows) #10526

Closed
sdirix opened this issue Dec 9, 2021 · 12 comments
Closed

Multi Window support (Floating windows) #10526

sdirix opened this issue Dec 9, 2021 · 12 comments
Labels
proposal feature proposals (potential future features) secondary-window issues related to multi or secondary window support

Comments

@sdirix
Copy link
Member

sdirix commented Dec 9, 2021

Summary

We would like to have fully featured multi window, a.k.a. floating window support in Theia. This issue proposes a specific approach and may serve as the Epic to implement this feature in full.

We evaluated the approach with a PoC and created an initial list of development tasks. Also we added a rough risk assessment for each of the determined challenges.

We are looking for feedback in regards to the suggested approach and the overall assessments. Feel free to add suggestions on how to solve the determined challenges and please notify us when we missed a whole concept or even only a minor detail.

Goal

  • Views shall be movable into secondary windows.
  • Views shall behave the same no matter whether they are contained in the main or in a secondary window

Status Quo

Theia offers a "New Window" action which opens an additional window. In this window a complete new Theia frontend is instantiated, including, among others, the inversify container, frontend application and application shell.

MultipleFrontendsDetail

The diagram above shows this case in a simplified way. The result is two completely separated frontend instances which do not share the same "UX context", e.g. they don't share a common selection or debugging state. They don't even share the same plugin process on the backend. They almost behave like two completely different instances of the same Theia application, with the difference that they share some singletons via the backend. For example the "running task" list is synchronized between these two frontends via the backend.

Downsides of Status Quo

  • Views can not be dragged and dropped between the windows
  • The same file or view can be opened in both windows, but they belong to their respective frontend instance. So for example the errors view will always be scoped to the corresponding window.

Overall it resembles a multi instance rather than a multi window approach

Comparison to other frameworks

A good implementation of multi window support can be found in Eclipse RCP.

EclipseRCPExample

Here the user is able to detach windows from the main window into (one or more) secondary windows. These share the same context as the main window. In the screenshot above you can see the detached console and debug view which share the same state with the main window. Ideally we would like to see similar behavior in Theia.

Looking at VS Code, multi window support is one of the most requested features for years.

Web technology

The reason why it's non trivial to support this for Theia and why VS Code does not yet offer this feature is the underlying tech stack. By relying on web technologies we also inherit their security model. Windows are almost completely isolated from each other.

By default the only way to communicate between windows is to send messages, either directly between them or via the Theia backend. This means that any shareable state needs to be fully serializable and a synchronization mechanism must be in place. Any functional callback must also be handled via messaging and corresponding proxy handlers.

This requires major architectural changes and influences the whole framework. Therefore a lot of effort is required to get this to work.

Alternative approaches

Before we settled on the suggested approach described below we also discussed alternative approaches.

Expand for more details

DOM Streaming

First on our list was to check whether there exists some DOM streaming techniques by which we would render the views in an invisible way within the main window and just project the results into the secondary windows.

DOMStreaming

However this was quickly aborted after we did not find a library or existing applications which use such a technique. Additionally there are some conceptual issues regarding the integration of user interaction. Also these might not even be possible for elements like canvas.

Multi window concept as a first citizen

Another more obvious approach is to enrich Theia in generally with the concept of multi window support. As the windows can only communicate via serialized messages, this concept must be baked in for every single window, service and use case handled by Theia.

SyncingService

Moving a widget between windows then requires to close said widget and reinstating the same in the secondary window. Each service the widget consumes, for example a selection service, but also any 3rd-party service must also be provided in the secondary window and behave the same in the secondary window as in the main window.

Conceptually there are then no more "real" singleton injections (each window has it's own Inversify container). A consequence could be that singletons exists in both windows and need to know how to share state. Questions like how to share a dynamically registered callback must then be answered. Another solution could be a parent-child concept for them by which for example only the "child" variant is injected in secondary windows. Depending on the use case this might not be possible transparently and consuming widgets might need to handle both "real" / "parent" and "proxy" / "child" injections.

Use cases need to be adjusted for multi windows too. For example switching the workspace of the main window should also update the workspace of the second window. The same is true for the debugging state, theming etc.. Each use case must be evaluated on their own what it means for them to be distributed over multiple windows and act accordingly.

Very likely this then requires developing not only a main frontend but also a secondary frontend with a reduced application shell and differently registered contributions. These different frontend instances should also use the same plugin host in the backend, to make sure the plugin views and other integrations like LSPs also behave the same between windows. It's also unclear how well the Monaco services can be synced.

Overall we had the impression that this approach requires so many changes to the overall framework that even producing a non-trivial PoC with views which consume at least some services is a huge undertaking. Therefore we went for the suggested approach discussed below.

Suggested approach

Browsers and Electron allow to create new windows which are controlled by the parent. Therefore we can access and manipulate the DOM of the secondary window from the main window.

We suggest to use this feature to render Widgets into secondary windows. Any interactions is automatically handled via the main window and therefore is executed in the same context.

SuggestedApproach

Using this approach allows us to reduce multi window support to a UI problem instead of elevating it to a core principle of the whole Theia framework. There are no additional services or frameworks involved. Most of the existing code doesn't need to be touched and many views work out of the box.

Proof of concept

We developed a proof of concept based on our suggested approach to check its feasibility and to identify challenges and risks and come up with an overall estimation of effort.

MultipleWindowsPoC

The proof of concept is available on branch "multi-window-poc" in our Theia fork. Please see the readme for instructions on how to build and run the PoC.

It showcases multi window support in the Electron example app. Views can be moved into a secondary window via the Sample menu > Move currently active view to new window action.

The PoC is not particularly polished but shows the feasibility of the suggested approach.

Identified challenges

PhosphorJS

For the PoC we removed a single line check in PhosphorJS

Styling

For the PoC we modified the frontend build one time to extract all CSS into a standalone stylesheet. This stylesheet is then loaded in secondary windows. We also execute Monaco loading in the second window to get the Monaco CSS.

Javascript Global Access

Theia code often makes use of Javascript globals like window and document. This may break functionality in secondary windows.

For example let's say a Widget renders a Button and some other element like a Searchbar. On click the Searchbar shall be cleared. As the callback code in the widget does not have direct access to the Searchbar the access may have been implemented by using the document global like this:

GlobalAccess

This breaks with our suggested approach, as the document of the Main window actually does not contain the Searchbar. Instead it's rendered in the DOM of the secondary window. We can't do any tricks here like extending the behavior of document, as these global variables are especially protected and can't be modified.

Code like this needs to be refactored for the suggested approach to work. In general, and especially when using React, it should be avoided to manipulate DOM elements directly.

However there might be cases where this is required or it would afford a lot of effort to be refactored. Luckily most code can also be adjusted by avoiding the global access, for example like this:

GlobalAccessFixed

By using this.node.ownerDocument instead of document within a widget the code can be generalized. Of course this fix might not apply to all situations, for example when accessing the DOM outside of a widget, but similar workarounds could be implemented.

In the PoC we added this fix for the searchbar clear button in the preferences widget

Views contributed by Plugins (VS Code extensions)

Conceptually these are the easiest views as they are completely encapsulated already anyway. To get them to work in secondary windows they just need to be connected properly.

Main window and the webview host (this is not the plugin host!) communicate via Window messages. These messages must be relayed between the main window and second window.

In the PoC we added a simple broadcasting in which all messages are relayed to all secondary windows and back to the main window

Monaco Editor

In the PoC: Moving a Monaco editor into a secondary window results in non-highlighted text which does not allow modifications.

Development Tasks

This sections lists a non-exhaustive list of development tasks for each of the overall topics

Styling

  • Extract CSS for secondary windows as part of the frontend webpack build or a separate build
  • Load and apply color customizations for secondary windows
  • Sync theme to secondary windows
  • Content in secondary windows should resize with it
  • Issue: Tree View styles result in 0 height within the PoC

Theia Base Framework

  • Extend ApplicationShell with the notion of widgets residing in secondary windows
  • Adapt code base to these shell changes where necessary
  • Adapt window specific use cases, like opening a dialog, to also support secondary windows
  • Provide a proper base widget for secondary windows
  • Adapt code base to handle being rendered in DOM of secondary windows
  • Implement a robust secondary window handler
  • Implement VS Code message bridge

Platform specifics

  • Allow Electron secondary windows with access rights to main window in a secure way
  • Adapt new window for Browser and Electron (e.g. remove Browser and Electron menu bars)

PhosphorJS

  • Some modifications to PhosphorJS are necessary to get the suggested approach to work at all
  • We expect further modifications are needed for polish work, e.g. drag and drop
  • Check single document assumptions which are baked in PhoshporJS and might affect this negatively

As an additional challenge we can't just upstream PhosphorJS changes as the project is archived and no longer under development. A workaround could be source level adaptions during the webpack build however this is probably not ideal on the long run.

Monaco

Background: Monaco is not built for the use case of rendering within a secondary DOM
In the PoC Monaco editors are rendered read only and permanently show an insert mouse icon on hover

  • Monaco editors needs to be fixed or a workaround needs to be found
  • Monaco views like "Output" need to be polished

Risk assessment

We don't expect any conceptual problems in regards to the development topics Styling, Theia Base Framework and Platform specifics.

There is medium risk in regards to PhosphorJS as we don't know about unknowns within Phosphor which could increase the effort for a polished experience by a lot and we need some solution to its development situation.

It's unclear how much effort would be needed to generalize Monaco and get it to work in secondary Theia windows. It's also unclear whether Microsoft would even accept these changes. Maybe there are alternative approaches with secondary Monaco instances in secondary windows to solve this problem differently which could also be evaluated. Therefore there is a high risk for a lot of effort which is currently hard to estimate.

There is a low risk of the Window API being modified in Chrome or other Browsers to disallow the amount of access we need to successfully use this feature. The API exists for a very long time and is similar to the one of IFrames. Removing the Window API would break a lot of web pages, however certainly not the majority. Therefore this risk can't be discarded completely.

Summary

Implementing multi window support with the suggested approach requires some work. However, except for Monaco, most of the work seems to have no conceptual risk and "just needs to be done". Overall the approach of implementing multi window support via the UI seems to encapsulate it rather well and does not require major architectural refactorings of the Theia framework.

Monaco is the most risky part however it's also the only one which could be considered optional. Having a polished multi-window feature without being able to place Monaco based views in secondary windows is already very useful.

We roughly estimated the effort to implement this in full in a clean and secure way to be in order of magnitude of 150 person days. However the work can be parallelized to a high degree and the feature could be released in an iterative manner, reducing the amount of work for the first iterations.

Going forward

We are looking for feedback in regards to the suggested approach and the overall assessments. Feel free to add suggestions on how to solve the determined challenges and please notify us when we missed a whole concept or even only a minor detail.

We also can't stem the work on this feature on our own and are therefore looking for volunteers to take over parts of the work. From our point of view implementing this feature would add a lot of value to the Theia framework and many adopters would benefit from it.

@msujew msujew added the proposal feature proposals (potential future features) label Dec 9, 2021
@colin-grant-work
Copy link
Contributor

I'm very much in favor of this feature - it would be a vast improvement over the current system, which requires you either to stretch your window to compare code side by side at reasonable sizes, or open multiple instances. The proof of concept is very impressive work. I would suggest that as a first step, we identify the areas of the code that make single-window assumptions and begin the process of refactoring those. That should allow us to validate some of the ideas that we'll need to implement the feature fully at very low risk.

@koegel
Copy link

koegel commented Dec 20, 2021

Thank you very much for your kind feedback, @colin-grant-work. We are currently working with different interested parties to gather enough resources to be able to get this going.
We only did a rough estimation but believe the overall effort to be quite significant - in the order of magnitude of several person months. Therefore we are currently discussing minimal viable products that already provide some value but are more manageable. One idea is to limit the multi window support to one specific view, e.g. terminal, or only to VS Code views.
In particular two areas of work can probably be distributed to many shoulders in an efficient way: Styling and Theia Base Framework.

@MatthewKhouzam
Copy link
Contributor

@colin-grant-work The plan is that we collaborate with @koegel and @sdirix to work on getting phosphor.js out of Theia. We are hoping others will join in to continue the feature as the tech debt is being removed.

@sdirix
Copy link
Member Author

sdirix commented Feb 9, 2022

MVP Proposal

To facilitate the development process we analyzed the proposal and distilled a MVP for the multi-window feature which requires significantly less effort than developing the feature in one go.

Concept

For the MVP we suggest to reduce the initial feature set via two measures:

  • External windows shall be simplified to only contain a single view. This reduces the UX requirements for secondary windows by a lot, for example no drag and drop of Views between Windows needs to be supported. It also keeps the involvement of PhosphorJS in the second window to a minimum which reduces a lot of risk and also avoids redundant effort in case PhosphorJS should be replaced in the future.
  • We use an opt-in approach for Views to be extractable to secondary Windows. For the MVP we suggest to only opt-in plugin based web views (i.e. Theia plugin / VS Code extension views) as they are already encapsulated by design and require minimal effort to work in secondary windows. As this already works very well in the existing PoC with very complex web views the risk is massively reduced for this feature.

Via the opt-in approach we lay the groundwork to ease the transition for Theia extensions views. They can be polished and enabled step by step. For example the Theia Terminal is a very good candidate for an "MVP++" as it's already managing its styling (almost) completely by itself, and therefore requires minimal adaptions to work in secondary windows.

Development Tasks

Styling

Almost all styling tasks of the original proposal can be skipped for the MVP. For example we don't need to export CSS, neither solve the theme and color syncing (including Monaco services) nor fix other problems present in the PoC.

The tasks for the MVP are:

  • Make sure the web view content grabs all available space in the window
  • Make sure the web views resize together with their secondary window.

Theia Base Framework

Some architecture tasks remain for the overall framework, however we don't need to walk through the whole code base to check every view interaction for compatibility with the secondary window approach.

The remaining tasks are:

  • ApplicationShell needs to handle widgets in secondary windows
  • Adapt code base to these shell changes where necessary
  • Implement VS Code message bridge
  • Implement opt-in mechanism for views to be extracted

These changes were already partially performed within the PoC and were sufficient to properly support web views. The opt-in mechanism could for example be realized via a TabBar action which is shown for extractable views.

Platform specifics

These tasks mostly remain the same:

  • Allow Electron secondary windows with access rights from the main window in a secure way
  • Adapt new window for Browser and Electron (e.g. remove Browser and Electron menu bars)

PhosphorJS

By reducing the scope of secondary windows to only host single views PhosphorJS is almost completely bypassed. By extracting views via a separate action and closing them via the Window mechanisms we don't need any adaptions to specifics like the PhosphorJS drag and drop.

The main remaining task is to modify the Webpack build to disable the document check when attaching Widgets to secondary windows.

Monaco

Monaco does not need to be touched at all for the MVP, removing a large risk factor.

Summary

This MVP for multi-window support only adds support for web views instead of "Theia views". This is obviously vastly reduced in scope, however in practice Theia based applications often use quite a number of VS Code extensions including web views.

We think this would be a great first step to bring the feature to life. Especially as we estimate the effort for the MVP at roughly 20-25 person days with a low to medium amount of risk. Compare this to the 150 person days for the full feature, including some parts which were identified as medium to high risk or which are somewhat blocked (PhosphorJS).

A good next step after the MVP could be to opt-in the Theia Terminal. It is architecturally relatively encapsulated and already works pretty well in the PoC.

@koegel
Copy link

koegel commented Apr 19, 2022

Update: We will soon open a PR with an implementation of the MVP as proposed by Stefan earlier.

@vince-fugnitto vince-fugnitto added the secondary-window issues related to multi or secondary window support label Sep 8, 2022
@dlarue
Copy link

dlarue commented Jan 29, 2023

Just an FYI, the Arduino IDE v2 uses the Eclipse-Theia platform and a few people have asked for undocking the serial monitor for display on a 2nd monitor. Currently the only solution is opening a 2nd IDE window and opening the serial monitor in that instance. A way to undock would be more elegant.

@msujew
Copy link
Member

msujew commented Jan 29, 2023

@dlarue Implementing this should be fairly straightforward. Adopters need to implement the ExtractableWidget interface and create a secondary-window entry for their Inversify configuration.

@dlarue
Copy link

dlarue commented Jan 29, 2023

Thanks @msujew , I'll pass this on to the Arduino devs.

@lucas-koehler
Copy link
Contributor

lucas-koehler commented Jan 30, 2023

Hello @dlarue,
adding to @msujew's excellent hints, I want to make sure you are aware that the current state has some limitations you might want to consider before using this in the IDE publicly.
The most important ones are:

The full list of secondary window related issues can be found via the secondary-window label.

As the contributor of the initial multi window support POC, you can gladly tag me in further questions you have or ask me directly :)

@trozen
Copy link

trozen commented Mar 23, 2023

Is there a way to test this with e.g. theia-blueprint?

@lucas-koehler
Copy link
Contributor

Hello @trozen ,
the current configuration of the theia-blueprint does not have this feature enabled at the moment. There are two ways to try this:

  1. Run the Theia browser example from the Theia repository. This has the secondary window support enabled
  2. Enabling the secondary window support in your Theia application by adding package @theia/secondary-window to the dependencies of the browser-app's package.json similar to the aforementioned example app. See here how this is done. With this, you can enable this in the blueprint application.

@sdirix
Copy link
Member Author

sdirix commented Apr 16, 2024

This is no longer a proposal but a reality. We have secondary window support for web views, terminals and now editors. This overarching issue can therefore be closed and remaining functionality gaps be handled in separate tickets.

@sdirix sdirix closed this as completed Apr 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal feature proposals (potential future features) secondary-window issues related to multi or secondary window support
Projects
None yet
Development

No branches or pull requests

9 participants