@@ -34,7 +34,7 @@ import { FrontendApplicationStateService } from '../frontend-application-state';
3434import { TabBarToolbarRegistry , TabBarToolbarFactory } from './tab-bar-toolbar' ;
3535import { ContextKeyService } from '../context-key-service' ;
3636import { Emitter } from '../../common/event' ;
37- import { waitForRevealed , waitForClosed , PINNED_CLASS } from '../widgets' ;
37+ import { waitForRevealed , waitForClosed , PINNED_CLASS , UnsafeWidgetUtilities } from '../widgets' ;
3838import { CorePreferences } from '../core-preferences' ;
3939import { BreadcrumbsRendererFactory } from '../breadcrumbs/breadcrumbs-renderer' ;
4040import { Deferred } from '../../common/promise-util' ;
@@ -85,10 +85,6 @@ export interface DockPanelRendererFactory {
8585 */
8686@injectable ( )
8787export class DockPanelRenderer implements DockLayout . IRenderer {
88-
89- @inject ( TheiaDockPanel . Factory )
90- protected readonly dockPanelFactory : TheiaDockPanel . Factory ;
91-
9288 readonly tabBarClasses : string [ ] = [ ] ;
9389
9490 private readonly onDidCreateTabBarEmitter = new Emitter < TabBar < Widget > > ( ) ;
@@ -175,6 +171,7 @@ interface WidgetDragState {
175171 leaveTimeout ?: number ;
176172}
177173
174+ export const MAXIMIZED_CLASS = 'theia-maximized' ;
178175/**
179176 * The application shell manages the top-level widgets of the application. Use this class to
180177 * add, remove, or activate a widget.
@@ -269,6 +266,8 @@ export class ApplicationShell extends Widget {
269266 protected initializedDeferred = new Deferred < void > ( ) ;
270267 initialized = this . initializedDeferred . promise ;
271268
269+ protected readonly maximizedElement : HTMLElement ;
270+
272271 /**
273272 * Construct a new application shell.
274273 */
@@ -286,6 +285,16 @@ export class ApplicationShell extends Widget {
286285 ) {
287286 super ( options as Widget . IOptions ) ;
288287
288+ this . maximizedElement = this . node . ownerDocument . createElement ( 'div' ) ;
289+ this . maximizedElement . style . position = 'fixed' ;
290+ this . maximizedElement . style . display = 'none' ;
291+ this . maximizedElement . style . left = '0px' ;
292+ this . maximizedElement . style . bottom = '0px' ;
293+ this . maximizedElement . style . right = '0px' ;
294+ this . maximizedElement . style . background = 'var(--theia-editor-background)' ;
295+ this . maximizedElement . style . zIndex = '2000' ;
296+ this . node . ownerDocument . body . appendChild ( this . maximizedElement ) ;
297+
289298 // Merge the user-defined application options with the default options
290299 this . options = {
291300 bottomPanel : {
@@ -301,6 +310,13 @@ export class ApplicationShell extends Widget {
301310 ...options ?. rightPanel || { }
302311 }
303312 } ;
313+ if ( corePreferences ) {
314+ corePreferences . onPreferenceChanged ( preference => {
315+ if ( preference . preferenceName === 'window.menuBarVisibility' && ( preference . newValue === 'visible' || preference . oldValue === 'visible' ) ) {
316+ this . handleMenuBarVisibility ( preference . newValue ) ;
317+ }
318+ } ) ;
319+ }
304320 }
305321
306322 @postConstruct ( )
@@ -561,7 +577,7 @@ export class ApplicationShell extends Widget {
561577 mode : 'multiple-document' ,
562578 renderer,
563579 spacing : 0
564- } ) ;
580+ } , area => this . doToggleMaximized ( area ) ) ;
565581 dockPanel . id = MAIN_AREA_ID ;
566582 dockPanel . widgetAdded . connect ( ( _ , widget ) => this . fireDidAddWidget ( widget ) ) ;
567583 dockPanel . widgetRemoved . connect ( ( _ , widget ) => this . fireDidRemoveWidget ( widget ) ) ;
@@ -658,7 +674,7 @@ export class ApplicationShell extends Widget {
658674 mode : 'multiple-document' ,
659675 renderer,
660676 spacing : 0
661- } ) ;
677+ } , area => this . doToggleMaximized ( area ) ) ;
662678 dockPanel . id = BOTTOM_AREA_ID ;
663679 dockPanel . widgetAdded . connect ( ( sender , widget ) => {
664680 this . refreshBottomPanelToggleButton ( ) ;
@@ -1178,9 +1194,6 @@ export class ApplicationShell extends Widget {
11781194 w . title . className = w . title . className . replace ( ' theia-mod-active' , '' ) ;
11791195 w = w . parent ;
11801196 }
1181- // Reset the z-index to the default
1182- // eslint-disable-next-line no-null/no-null
1183- this . setZIndex ( oldValue . node , null ) ;
11841197 }
11851198 if ( newValue ) {
11861199 let w : Widget | null = newValue ;
@@ -1203,11 +1216,6 @@ export class ApplicationShell extends Widget {
12031216 // if widget was undefined, we wouldn't have gotten a panel back before
12041217 panel . markAsCurrent ( widget ! . title ) ;
12051218 }
1206- // Add checks to ensure that the 'sash' for left panel is displayed correctly
1207- if ( newValue . node . className === 'lm-Widget theia-view-container lm-DockPanel-widget' ) {
1208- // Set the z-index so elements with `position: fixed` contained in the active widget are displayed correctly
1209- this . setZIndex ( newValue . node , '1' ) ;
1210- }
12111219
12121220 // activate another widget if an active widget will be closed
12131221 const onCloseRequest = newValue [ 'onCloseRequest' ] ;
@@ -1237,17 +1245,6 @@ export class ApplicationShell extends Widget {
12371245 this . onDidChangeActiveWidgetEmitter . fire ( args ) ;
12381246 }
12391247
1240- /**
1241- * Set the z-index of the given element and its ancestors to the value `z`.
1242- */
1243- private setZIndex ( element : HTMLElement , z : string | null ) : void {
1244- element . style . zIndex = z || '' ;
1245- const parent = element . parentElement ;
1246- if ( parent && parent !== this . node ) {
1247- this . setZIndex ( parent , z ) ;
1248- }
1249- }
1250-
12511248 /**
12521249 * Track the given widget so it is considered in the `current` and `active` state of the shell.
12531250 */
@@ -2118,11 +2115,75 @@ export class ApplicationShell extends Widget {
21182115 toggleMaximized ( widget : Widget | undefined = this . currentWidget ) : void {
21192116 const area = widget && this . getAreaPanelFor ( widget ) ;
21202117 if ( area instanceof TheiaDockPanel && ( area === this . mainPanel || area === this . bottomPanel ) ) {
2121- area . toggleMaximized ( ) ;
2118+ this . doToggleMaximized ( area ) ;
21222119 this . revealWidget ( widget ! . id ) ;
21232120 }
21242121 }
21252122
2123+ protected handleMenuBarVisibility ( newValue : string ) : void {
2124+ if ( newValue === 'visible' ) {
2125+ const topRect = this . topPanel . node . getBoundingClientRect ( ) ;
2126+ this . maximizedElement . style . top = `${ topRect . bottom } px` ;
2127+ } else {
2128+ this . maximizedElement . style . removeProperty ( 'top' ) ;
2129+ }
2130+ }
2131+
2132+ protected readonly onDidToggleMaximizedEmitter = new Emitter < Widget > ( ) ;
2133+ readonly onDidToggleMaximized = this . onDidToggleMaximizedEmitter . event ;
2134+
2135+ protected unmaximize : ( ( ) => void ) | undefined ;
2136+ doToggleMaximized ( area : TheiaDockPanel ) : void {
2137+ if ( this . unmaximize ) {
2138+ this . unmaximize ( ) ;
2139+ this . unmaximize = undefined ;
2140+ return ;
2141+ }
2142+
2143+ const removedListener = ( ) => {
2144+ if ( ! area . widgets ( ) . next ( ) . value ) {
2145+ this . doToggleMaximized ( area ) ;
2146+ }
2147+ } ;
2148+
2149+ const parent = area . parent as SplitPanel ;
2150+ const layout = area . parent ?. layout as SplitLayout ;
2151+ const sizes = layout . relativeSizes ( ) . slice ( ) ;
2152+ const stretch = SplitPanel . getStretch ( area ) ;
2153+ const index = parent . widgets . indexOf ( area ) ;
2154+ parent . layout ?. removeWidget ( area ) ;
2155+
2156+ // eslint-disable-next-line no-null/no-null
2157+ this . maximizedElement . style . display = 'block' ;
2158+ area . addClass ( MAXIMIZED_CLASS ) ;
2159+ const topRect = this . topPanel . node . getBoundingClientRect ( ) ;
2160+ UnsafeWidgetUtilities . attach ( area , this . maximizedElement ) ;
2161+ this . maximizedElement . style . top = `${ topRect . bottom } px` ;
2162+ area . fit ( ) ;
2163+ const observer = new ResizeObserver ( entries => {
2164+ area . fit ( ) ;
2165+ } ) ;
2166+ observer . observe ( this . maximizedElement ) ;
2167+
2168+ this . unmaximize = ( ) => {
2169+ observer . unobserve ( this . maximizedElement ) ;
2170+ observer . disconnect ( ) ;
2171+ this . maximizedElement . style . display = 'none' ;
2172+ area . removeClass ( MAXIMIZED_CLASS ) ;
2173+ if ( area . isAttached ) {
2174+ UnsafeWidgetUtilities . detach ( area ) ;
2175+ }
2176+ parent ?. insertWidget ( index , area ) ;
2177+ SplitPanel . setStretch ( area , stretch ) ;
2178+ layout . setRelativeSizes ( sizes ) ;
2179+ parent . fit ( ) ;
2180+ this . onDidToggleMaximizedEmitter . fire ( area ) ;
2181+ area . widgetRemoved . disconnect ( removedListener ) ;
2182+ } ;
2183+
2184+ area . widgetRemoved . connect ( removedListener ) ;
2185+ this . onDidToggleMaximizedEmitter . fire ( area ) ;
2186+ }
21262187}
21272188
21282189/**
0 commit comments