From bcc53aad7f1f1ca5f52f40ff0140aae39407124d Mon Sep 17 00:00:00 2001 From: Vivien Jovet Date: Thu, 5 Mar 2020 10:51:48 +0800 Subject: [PATCH] [#7268] Adjust behavior of all close tab commands so that they respect the widget.title.closable propertySigned-off-by: Vivien Jovet --- .../src/browser/api-samples-contribution.ts | 16 ++++++- .../browser/api-samples-frontend-module.ts | 10 ++++- .../api-samples/src/browser/sample-view.tsx | 43 +++++++++++++++++++ .../browser/common-frontend-contribution.ts | 41 ++++++++++++------ 4 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 examples/api-samples/src/browser/sample-view.tsx diff --git a/examples/api-samples/src/browser/api-samples-contribution.ts b/examples/api-samples/src/browser/api-samples-contribution.ts index 14c856fa1a848..df9eab0a59cb8 100644 --- a/examples/api-samples/src/browser/api-samples-contribution.ts +++ b/examples/api-samples/src/browser/api-samples-contribution.ts @@ -16,8 +16,9 @@ import { injectable, inject } from 'inversify'; import { Command, CommandContribution, CommandRegistry, CommandHandler } from '@theia/core'; -import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { AbstractViewContribution, FrontendApplicationContribution } from '@theia/core/lib/browser'; import { SampleDynamicLabelProviderContribution } from './sample-dynamic-label-provider-contribution'; +import { SampleView } from '@theia/api-samples/lib/browser/sample-view'; export namespace ExampleLabelProviderCommands { const EXAMPLE_CATEGORY = 'Examples'; @@ -53,3 +54,16 @@ export class ExampleLabelProviderCommandHandler implements CommandHandler { } } + +@injectable() +export class SampleViewContribution extends AbstractViewContribution { + constructor() { + super({ + widgetId: SampleView.ID, + widgetName: 'Sample', + defaultWidgetOptions: { + area: 'main' + } + }); + } +} diff --git a/examples/api-samples/src/browser/api-samples-frontend-module.ts b/examples/api-samples/src/browser/api-samples-frontend-module.ts index 2c2fe27d83186..8fa887b290204 100644 --- a/examples/api-samples/src/browser/api-samples-frontend-module.ts +++ b/examples/api-samples/src/browser/api-samples-frontend-module.ts @@ -17,12 +17,20 @@ import { ContainerModule } from 'inversify'; import { CommandContribution } from '@theia/core'; import { LabelProviderContribution } from '@theia/core/lib/browser/label-provider'; -import { ApiSamplesContribution } from './api-samples-contribution'; +import { ApiSamplesContribution, SampleViewContribution } from './api-samples-contribution'; import { SampleDynamicLabelProviderContribution } from './sample-dynamic-label-provider-contribution'; +import { bindViewContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { SampleView } from './sample-view'; export default new ContainerModule(bind => { bind(CommandContribution).to(ApiSamplesContribution).inSingletonScope(); bind(SampleDynamicLabelProviderContribution).toSelf().inSingletonScope(); bind(LabelProviderContribution).toService(SampleDynamicLabelProviderContribution); + bindViewContribution(bind, SampleViewContribution); + bind(SampleView).toSelf(); + bind(WidgetFactory).toDynamicValue(ctx => ({ + id: SampleView.ID, + createWidget: () => ctx.container.get(SampleView) + })); }); diff --git a/examples/api-samples/src/browser/sample-view.tsx b/examples/api-samples/src/browser/sample-view.tsx new file mode 100644 index 0000000000000..bd80d2e5eb75e --- /dev/null +++ b/examples/api-samples/src/browser/sample-view.tsx @@ -0,0 +1,43 @@ +/******************************************************************************** + * Copyright (C) 2019 Arm and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { ReactWidget } from '@theia/core/lib/browser'; +import { injectable, postConstruct } from 'inversify'; +import * as React from 'react'; + +@injectable() +export class SampleView extends ReactWidget { + static readonly ID = 'sampleView'; + + @postConstruct() + init(): void { + this.id = SampleView.ID; + this.title.caption = 'Sample'; + this.title.label = 'Sample'; + this.title.iconClass = 'fa fa-window-maximize'; + this.title.closable = true; + this.update(); + } + + protected render(): React.ReactNode { + return ( +
+ Closeable + this.title.closable = e.target.checked} /> +
+ ); + } +} diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 039787870f74c..59a9c13492497 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -520,7 +520,14 @@ export class CommonFrontendContribution implements FrontendApplicationContributi execute: () => this.shell.activatePreviousTab() }); commandRegistry.registerCommand(CommonCommands.CLOSE_TAB, { - isEnabled: (event?: Event) => this.findTabBar(event) !== undefined, + isEnabled: (event?: Event) => { + const tabBar = this.findTabBar(event); + if (!tabBar) { + return false; + } + const currentTitle = this.findTitle(tabBar, event); + return currentTitle !== undefined && currentTitle.closable; + }, execute: (event?: Event) => { const tabBar = this.findTabBar(event)!; const currentTitle = this.findTitle(tabBar, event); @@ -530,19 +537,23 @@ export class CommonFrontendContribution implements FrontendApplicationContributi commandRegistry.registerCommand(CommonCommands.CLOSE_OTHER_TABS, { isEnabled: (event?: Event) => { const tabBar = this.findTabBar(event); - return tabBar !== undefined && tabBar.titles.length > 1; + if (!tabBar) { + return false; + } + const currentTitle = this.findTitle(tabBar, event); + return tabBar.titles.some(title => title !== currentTitle && title.closable); }, execute: (event?: Event) => { const tabBar = this.findTabBar(event)!; const currentTitle = this.findTitle(tabBar, event); const area = this.shell.getAreaFor(tabBar)!; - this.shell.closeTabs(area, title => title !== currentTitle); + this.shell.closeTabs(area, title => title !== currentTitle && title.closable); } }); commandRegistry.registerCommand(CommonCommands.CLOSE_RIGHT_TABS, { isEnabled: (event?: Event) => { const tabBar = this.findTabBar(event); - return tabBar !== undefined && tabBar.currentIndex < tabBar.titles.length - 1; + return tabBar !== undefined && tabBar.titles.some((title, index) => index > tabBar.currentIndex && title.closable); }, isVisible: (event?: Event) => { const area = this.findTabArea(event); @@ -551,32 +562,38 @@ export class CommonFrontendContribution implements FrontendApplicationContributi execute: (event?: Event) => { const tabBar = this.findTabBar(event)!; const currentIndex = tabBar.currentIndex; - this.shell.closeTabs(tabBar, (_, index) => index > currentIndex); + this.shell.closeTabs(tabBar, (title, index) => index > currentIndex && title.closable); } }); commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_TABS, { - isEnabled: (event?: Event) => this.findTabBar(event) !== undefined, - execute: (event?: Event) => this.shell.closeTabs(this.findTabArea(event)!) + isEnabled: (event?: Event) => { + const tabBar = this.findTabBar(event); + return tabBar !== undefined && tabBar.titles.some(title => title.closable); + }, + execute: (event?: Event) => this.shell.closeTabs(this.findTabArea(event)!, title => title.closable) }); commandRegistry.registerCommand(CommonCommands.CLOSE_MAIN_TAB, { - isEnabled: () => this.shell.getCurrentWidget('main') !== undefined, + isEnabled: () => { + const currentWidget = this.shell.getCurrentWidget('main'); + return currentWidget !== undefined && currentWidget.title.closable; + }, execute: () => this.shell.getCurrentWidget('main')!.close() }); commandRegistry.registerCommand(CommonCommands.CLOSE_OTHER_MAIN_TABS, { isEnabled: () => { const tabBars = this.shell.mainAreaTabBars; - return tabBars.length > 1 || tabBars.length === 1 && tabBars[0].titles.length > 1; + return tabBars.length > 1 || tabBars.length === 1 && tabBars[0].titles.some(title => title.closable); }, execute: () => { const currentWidget = this.shell.getCurrentWidget('main'); if (currentWidget !== undefined) { - this.shell.closeTabs('main', title => title.owner !== currentWidget); + this.shell.closeTabs('main', title => title.owner !== currentWidget && title.closable); } } }); commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_MAIN_TABS, { - isEnabled: () => this.shell.mainAreaTabBars.find(tb => tb.titles.length > 0) !== undefined, - execute: () => this.shell.closeTabs('main') + isEnabled: () => this.shell.mainAreaTabBars.some(tb => tb.titles.some(title => title.closable)), + execute: () => this.shell.closeTabs('main', title => title.closable) }); commandRegistry.registerCommand(CommonCommands.COLLAPSE_PANEL, { isEnabled: (event?: Event) => ApplicationShell.isSideArea(this.findTabArea(event)),