mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-08 20:06:32 +00:00
fix: debug widget layout updates
Use customized `PanelLayout#removeWidget` and `PanelLayout#insertWidget` logic for the layout updates. The customized functions ensure no side effect if adding/removing the widget to/from the layout but it's already present/absent. Unlike the default [`PanelLayout#removeWidget`](9f5e11025b/packages/widgets/src/panellayout.ts (L154-L156)
) behavior, do not try to remove the widget if it's not present (has a negative index). Otherwise, required widgets might be removed based on the default [`ArrayExt#removeAt`](9f5e11025b/packages/algorithm/src/array.ts (L1075-L1077)
) behavior. Closes: arduino/arduino-ide#2354 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
8c1ffe9c5f
commit
470db5606e
@ -1,8 +1,5 @@
|
|||||||
import {
|
import { codicon } from '@theia/core/lib/browser/widgets/widget';
|
||||||
codicon,
|
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||||
PanelLayout,
|
|
||||||
Widget,
|
|
||||||
} from '@theia/core/lib/browser/widgets/widget';
|
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
injectable,
|
injectable,
|
||||||
@ -10,6 +7,10 @@ import {
|
|||||||
} from '@theia/core/shared/inversify';
|
} from '@theia/core/shared/inversify';
|
||||||
import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||||
import { DebugDisabledStatusMessageSource } from '../../contributions/debug';
|
import { DebugDisabledStatusMessageSource } from '../../contributions/debug';
|
||||||
|
import {
|
||||||
|
removeWidgetIfPresent,
|
||||||
|
unshiftWidgetIfNotPresent,
|
||||||
|
} from '../dialogs/widgets';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class DebugWidget extends TheiaDebugWidget {
|
export class DebugWidget extends TheiaDebugWidget {
|
||||||
@ -38,12 +39,10 @@ export class DebugWidget extends TheiaDebugWidget {
|
|||||||
this.messageNode.textContent = message ?? '';
|
this.messageNode.textContent = message ?? '';
|
||||||
const enabled = !message;
|
const enabled = !message;
|
||||||
updateVisibility(enabled, this.toolbar, this.sessionWidget);
|
updateVisibility(enabled, this.toolbar, this.sessionWidget);
|
||||||
if (this.layout instanceof PanelLayout) {
|
if (enabled) {
|
||||||
if (enabled) {
|
removeWidgetIfPresent(this.layout, this.statusMessageWidget);
|
||||||
this.layout.removeWidget(this.statusMessageWidget);
|
} else {
|
||||||
} else {
|
unshiftWidgetIfNotPresent(this.layout, this.statusMessageWidget);
|
||||||
this.layout.insertWidget(0, this.statusMessageWidget);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.title.iconClass = enabled ? codicon('debug-alt') : 'fa fa-ban'; // TODO: find a better icon?
|
this.title.iconClass = enabled ? codicon('debug-alt') : 'fa fa-ban'; // TODO: find a better icon?
|
||||||
});
|
});
|
||||||
|
51
arduino-ide-extension/src/browser/theia/dialogs/widgets.ts
Normal file
51
arduino-ide-extension/src/browser/theia/dialogs/widgets.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
Layout,
|
||||||
|
PanelLayout,
|
||||||
|
Widget,
|
||||||
|
} from '@theia/core/shared/@phosphor/widgets';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Removes the widget from the layout if the `layout` is a `PanelLayout` and the widget is present in the layout.
|
||||||
|
* Otherwise, it's NOOP
|
||||||
|
* @param layout the layout to remove the widget from. Must be a `PanelLayout`.
|
||||||
|
* @param toRemove the widget to remove from the layout
|
||||||
|
*/
|
||||||
|
export function removeWidgetIfPresent(
|
||||||
|
layout: Layout | null,
|
||||||
|
toRemove: Widget
|
||||||
|
): void {
|
||||||
|
if (layout instanceof PanelLayout) {
|
||||||
|
const index = layout.widgets.indexOf(toRemove);
|
||||||
|
if (index < 0) {
|
||||||
|
// Unlike the default `PanelLayout#removeWidget` behavior, (https://github.com/phosphorjs/phosphor/blob/9f5e11025b62d2c4a6fb59e2681ae1ed323dcde4/packages/widgets/src/panellayout.ts#L154-L156)
|
||||||
|
// do not try to remove widget if it's not present (the index is negative).
|
||||||
|
// Otherwise, required widgets could be removed based on the default ArrayExt behavior (https://github.com/phosphorjs/phosphor/blob/9f5e11025b62d2c4a6fb59e2681ae1ed323dcde4/packages/algorithm/src/array.ts#L1075-L1077)
|
||||||
|
// See https://github.com/arduino/arduino-ide/issues/2354 for more details.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layout.removeWidget(toRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Inserts the widget to the `0` index of the layout if the `layout` is a `PanelLayout` and the widget is not yet part of the layout.
|
||||||
|
* Otherwise, it's NOOP
|
||||||
|
* @param layout the layout to add the widget to. Must be a `PanelLayout`.
|
||||||
|
* @param toAdd the widget to add to the layout
|
||||||
|
*/
|
||||||
|
export function unshiftWidgetIfNotPresent(
|
||||||
|
layout: Layout | null,
|
||||||
|
toAdd: Widget
|
||||||
|
): void {
|
||||||
|
if (layout instanceof PanelLayout) {
|
||||||
|
const index = layout.widgets.indexOf(toAdd);
|
||||||
|
if (index >= 0) {
|
||||||
|
// Do not try to add the widget to the layout if it's already present.
|
||||||
|
// This is the counterpart logic of the `removeWidgetIfPresent` function.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layout.insertWidget(0, toAdd);
|
||||||
|
}
|
||||||
|
}
|
121
arduino-ide-extension/src/test/browser/widgets.test.ts
Normal file
121
arduino-ide-extension/src/test/browser/widgets.test.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
Disposable,
|
||||||
|
DisposableCollection,
|
||||||
|
} from '@theia/core/lib/common/disposable';
|
||||||
|
import type { PanelLayout, Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import type {
|
||||||
|
removeWidgetIfPresent,
|
||||||
|
unshiftWidgetIfNotPresent,
|
||||||
|
} from '../../browser/theia/dialogs/widgets';
|
||||||
|
|
||||||
|
describe('widgets', () => {
|
||||||
|
let toDispose: DisposableCollection;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const disableJSDOM =
|
||||||
|
require('@theia/core/lib/browser/test/jsdom').enableJSDOM();
|
||||||
|
toDispose = new DisposableCollection(
|
||||||
|
Disposable.create(() => disableJSDOM())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => toDispose.dispose());
|
||||||
|
|
||||||
|
describe('removeWidgetIfPresent', () => {
|
||||||
|
let testMe: typeof removeWidgetIfPresent;
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
() =>
|
||||||
|
(testMe =
|
||||||
|
require('../../browser/theia/dialogs/widgets').removeWidgetIfPresent)
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should remove the widget if present', () => {
|
||||||
|
const layout = newPanelLayout();
|
||||||
|
const widget = newWidget();
|
||||||
|
layout.addWidget(widget);
|
||||||
|
const toRemoveWidget = newWidget();
|
||||||
|
layout.addWidget(toRemoveWidget);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget, toRemoveWidget]);
|
||||||
|
|
||||||
|
testMe(layout, toRemoveWidget);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be noop if the widget is not part of the layout', () => {
|
||||||
|
const layout = newPanelLayout();
|
||||||
|
const widget = newWidget();
|
||||||
|
layout.addWidget(widget);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget]);
|
||||||
|
|
||||||
|
testMe(layout, newWidget());
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unshiftWidgetIfNotPresent', () => {
|
||||||
|
let testMe: typeof unshiftWidgetIfNotPresent;
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
() =>
|
||||||
|
(testMe =
|
||||||
|
require('../../browser/theia/dialogs/widgets').unshiftWidgetIfNotPresent)
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should unshift the widget if not present', () => {
|
||||||
|
const layout = newPanelLayout();
|
||||||
|
const widget = newWidget();
|
||||||
|
layout.addWidget(widget);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget]);
|
||||||
|
|
||||||
|
const toAdd = newWidget();
|
||||||
|
testMe(layout, toAdd);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([toAdd, widget]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be NOOP if widget is already part of the layout (at 0 index)', () => {
|
||||||
|
const layout = newPanelLayout();
|
||||||
|
const toAdd = newWidget();
|
||||||
|
layout.addWidget(toAdd);
|
||||||
|
const widget = newWidget();
|
||||||
|
layout.addWidget(widget);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([toAdd, widget]);
|
||||||
|
|
||||||
|
testMe(layout, toAdd);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([toAdd, widget]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be NOOP if widget is already part of the layout (at >0 index)', () => {
|
||||||
|
const layout = newPanelLayout();
|
||||||
|
const widget = newWidget();
|
||||||
|
layout.addWidget(widget);
|
||||||
|
const toAdd = newWidget();
|
||||||
|
layout.addWidget(toAdd);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget, toAdd]);
|
||||||
|
|
||||||
|
testMe(layout, toAdd);
|
||||||
|
|
||||||
|
expect(layout.widgets).to.be.deep.equal([widget, toAdd]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function newWidget(): Widget {
|
||||||
|
const { Widget } = require('@theia/core/shared/@phosphor/widgets');
|
||||||
|
return new Widget();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newPanelLayout(): PanelLayout {
|
||||||
|
const { PanelLayout } = require('@theia/core/shared/@phosphor/widgets');
|
||||||
|
return new PanelLayout();
|
||||||
|
}
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user