feat: gready rendering

This commit is contained in:
mathuo 2024-01-03 16:25:35 +00:00
parent 6a620d4088
commit 9b1c366ce5
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
11 changed files with 182 additions and 21 deletions

View File

@ -0,0 +1,8 @@
import { OverlayRenderContainer } from '../overlayRenderContainer';
describe('overlayRenderContainer', () => {
test('abc', () => {
const el = document.createElement('div');
const cut = new OverlayRenderContainer(el);
});
});

View File

@ -5,7 +5,7 @@ import { MutableDisposable } from '../lifecycle';
import { DockviewPanel, IDockviewPanel } from '../dockview/dockviewPanel';
import { DockviewComponent } from '../dockview/dockviewComponent';
import { Position } from '../dnd/droptarget';
import { DockviewPanelRenderer } from '../dockview/components/greadyRenderContainer';
import { DockviewPanelRenderer } from '../overlayRenderContainer';
export interface TitleEvent {
readonly title: string;

View File

@ -58,12 +58,6 @@ export class ContentContainer
this.addDisposables(this._onDidFocus, this._onDidBlur);
// for hosted containers
// 1) register a drop target on the host
// 2) register window dragStart events to disable pointer events
// 3) register dragEnd events
// 4) register mouseMove events (if no buttons are present we take this as a dragEnd event)
this.dropTarget = new Droptarget(this.element, {
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
canDisplayOverlay: (event, position) => {
@ -125,7 +119,7 @@ export class ContentContainer
switch (panel.api.renderer) {
case 'onlyWhenVisibile':
this.accessor.greadyRenderContainer.remove(panel);
this.accessor.overlayRenderContainer.remove(panel);
if (isActive) {
if (this.panel) {
this._element.appendChild(
@ -142,7 +136,7 @@ export class ContentContainer
this._element.removeChild(panel.view.content.element);
}
container =
this.accessor.greadyRenderContainer.setReferenceContentContainer(
this.accessor.overlayRenderContainer.setReferenceContentContainer(
panel,
this
);
@ -201,7 +195,7 @@ export class ContentContainer
switch (renderer) {
case 'always':
container =
this.accessor.greadyRenderContainer.setReferenceContentContainer(
this.accessor.overlayRenderContainer.setReferenceContentContainer(
panel,
this
);

View File

@ -11,7 +11,7 @@
z-index: 1;
}
.dv-gready-render-container {
.dv-overlay-render-container {
position: relative;
}
}

View File

@ -56,9 +56,18 @@ import {
TabDragEvent,
} from './components/titlebar/tabsContainer';
import {
GreadyRenderContainer,
OverlayRenderContainer,
DockviewPanelRenderer,
<<<<<<< Updated upstream
} from './components/greadyRenderContainer';
=======
} from '../overlayRenderContainer';
import { DockviewPopoutGroupPanel } from './dockviewPopoutGroupPanel';
import {
DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE,
DEFAULT_FLOATING_GROUP_POSITION,
} from '../constants';
>>>>>>> Stashed changes
const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
@ -249,7 +258,7 @@ export class DockviewComponent
private _options: Exclude<DockviewComponentOptions, 'orientation'>;
private watermark: IWatermarkRenderer | null = null;
readonly greadyRenderContainer: GreadyRenderContainer;
readonly overlayRenderContainer: OverlayRenderContainer;
private readonly _onWillDragPanel = new Emitter<TabDragEvent>();
readonly onWillDragPanel: Event<TabDragEvent> = this._onWillDragPanel.event;
@ -319,16 +328,16 @@ export class DockviewComponent
});
const gready = document.createElement('div');
gready.className = 'dv-gready-render-container';
gready.className = 'dv-overlay-render-container';
this.gridview.element.appendChild(gready);
this.greadyRenderContainer = new GreadyRenderContainer(gready);
this.overlayRenderContainer = new OverlayRenderContainer(gready);
toggleClass(this.gridview.element, 'dv-dockview', true);
toggleClass(this.element, 'dv-debug', !!options.debug);
this.addDisposables(
this.greadyRenderContainer,
this.overlayRenderContainer,
this._onWillDragPanel,
this._onWillDragGroup,
this._onDidActivePanelChange,
@ -1059,7 +1068,7 @@ export class DockviewComponent
group.model.removePanel(panel);
if (!options.skipDispose) {
this.greadyRenderContainer.remove(panel);
this.overlayRenderContainer.remove(panel);
panel.dispose();
}

View File

@ -9,7 +9,7 @@ import { CompositeDisposable, IDisposable } from '../lifecycle';
import { IPanel, PanelUpdateEvent, Parameters } from '../panel/types';
import { IDockviewPanelModel } from './dockviewPanelModel';
import { DockviewComponent } from './dockviewComponent';
import { DockviewPanelRenderer } from './components/greadyRenderContainer';
import { DockviewPanelRenderer } from '../overlayRenderContainer';
export interface IDockviewPanel extends IDisposable, IPanel {
readonly view: IDockviewPanelModel;

View File

@ -20,7 +20,7 @@ import {
FrameworkFactory,
} from '../panel/componentFactory';
import { DockviewGroupPanelApi } from '../api/dockviewGroupPanelApi';
import { DockviewPanelRenderer } from './components/greadyRenderContainer';
import { DockviewPanelRenderer } from '../overlayRenderContainer';
export interface IHeaderActionsRenderer extends IDisposable {
readonly element: HTMLElement;

View File

@ -5,7 +5,7 @@ import { DockviewApi } from '../api/component.api';
import { Event } from '../events';
import { Optional } from '../types';
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
import { DockviewPanelRenderer } from './components/greadyRenderContainer';
import { DockviewPanelRenderer } from '../overlayRenderContainer';
export enum DockviewDropTargets {
Tab,

View File

@ -49,7 +49,7 @@ export * from './splitview/splitviewPanel';
export * from './paneview/paneviewPanel';
export * from './dockview/types';
export { DockviewPanelRenderer } from './dockview/components/greadyRenderContainer';
export { DockviewPanelRenderer } from './overlayRenderContainer';
export {
Position,

View File

@ -0,0 +1,150 @@
import { DragAndDropObserver } from './dnd/dnd';
import { Droptarget } from './dnd/droptarget';
import { getDomNodePagePosition, toggleClass } from './dom';
import { CompositeDisposable, Disposable, IDisposable } from './lifecycle';
import { IDockviewPanel } from './dockview/dockviewPanel';
export type DockviewPanelRenderer = 'onlyWhenVisibile' | 'always';
export interface IRenderable {
readonly element: HTMLElement;
readonly dropTarget: Droptarget;
}
function createFocusableElement(): HTMLDivElement {
const element = document.createElement('div');
element.tabIndex = -1;
return element;
}
export class OverlayRenderContainer extends CompositeDisposable {
private readonly map: Record<
string,
{ disposable: IDisposable; element: HTMLElement }
> = {};
get allIds(): string[] {
return Object.keys(this.map);
}
constructor(private readonly element: HTMLElement) {
super();
this.addDisposables({
dispose: () => {
for (const value of Object.values(this.map)) {
value.disposable.dispose();
}
},
});
}
remove(panel: IDockviewPanel): boolean {
if (this.map[panel.api.id]) {
this.map[panel.api.id].disposable.dispose();
delete this.map[panel.api.id];
return true;
}
return false;
}
setReferenceContentContainer(
panel: IDockviewPanel,
referenceContainer: IRenderable
): HTMLElement {
if (!this.map[panel.api.id]) {
const element = createFocusableElement();
element.className = 'dv-render-overlay';
this.map[panel.api.id] = {
disposable: Disposable.NONE,
element,
};
}
this.map[panel.api.id]?.disposable.dispose();
const focusContainer = this.map[panel.api.id].element;
if (panel.view.content.element.parentElement !== focusContainer) {
focusContainer.appendChild(panel.view.content.element);
}
if (focusContainer.parentElement !== this.element) {
this.element.appendChild(focusContainer);
}
const resize = () => {
// TODO propagate position to avoid getDomNodePagePosition calls, possible performance bottleneck?
const box = getDomNodePagePosition(referenceContainer.element);
const box2 = getDomNodePagePosition(this.element);
focusContainer.style.left = `${box.left - box2.left}px`;
focusContainer.style.top = `${box.top - box2.top}px`;
focusContainer.style.width = `${box.width}px`;
focusContainer.style.height = `${box.height}px`;
toggleClass(
focusContainer,
'dv-render-overlay-float',
panel.group.api.location === 'floating'
);
};
const disposable = new CompositeDisposable(
/**
* since container is positioned absoutely we must explicitly forward
* the dnd events for the expect behaviours to continue to occur in terms of dnd
*/
new DragAndDropObserver(focusContainer, {
onDragEnd: (e) => {
referenceContainer.dropTarget.dnd.onDragEnd(e);
},
onDragEnter: (e) => {
referenceContainer.dropTarget.dnd.onDragEnter(e);
},
onDragLeave: (e) => {
referenceContainer.dropTarget.dnd.onDragLeave(e);
},
onDrop: (e) => {
referenceContainer.dropTarget.dnd.onDrop(e);
},
onDragOver: (e) => {
referenceContainer.dropTarget.dnd.onDragOver(e);
},
}),
panel.api.onDidVisibilityChange((event) => {
/**
* Control the visibility of the content, however even when not visible (display: none)
* the content is still maintained within the DOM hence DOM specific attributes
* such as scroll position are maintained when next made visible.
*/
focusContainer.style.display = event.isVisible ? '' : 'none';
}),
panel.api.onDidDimensionsChange(() => {
resize();
}),
{
dispose: () => {
focusContainer.removeChild(panel.view.content.element);
this.element.removeChild(focusContainer);
},
}
);
queueMicrotask(() => {
if (this.isDisposed) {
return;
}
/**
* wait until everything has finished in the current stack-frame call before
* calling the first resize as other size-altering events may still occur before
* the end of the stack-frame.
*/
resize();
});
this.map[panel.api.id].disposable = disposable;
return focusContainer;
}
}