Skip to content

Commit 098c78c

Browse files
authored
feat: develop open editors window (#183)
* feat: develop open editors window * feat: editorTree support contextMenus & toolbar * fix: fix explorer openEditor i18n
1 parent ad1bbc1 commit 098c78c

File tree

22 files changed

+764
-9
lines changed

22 files changed

+764
-9
lines changed

src/common/id.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export const ID_SIDE_BAR = 'sidebar';
55
export const ID_EXPLORER = 'explorer';
66
export const ID_STATUS_BAR = 'statusBar';
77
export const ID_FOLDER_TREE = 'folderTree';
8+
export const ID_EDITOR_TREE = 'editorTree';
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import 'reflect-metadata';
2+
import React from 'react';
3+
import { Controller } from 'mo/react/controller';
4+
import { container, singleton } from 'tsyringe';
5+
import {
6+
builtInEditorTreeContextMenu,
7+
builtInEditorTreeHeaderContextMenu,
8+
EditorTreeEvent,
9+
} from 'mo/model/workbench/explorer/editorTree';
10+
import { EditorService, ExplorerService, FolderTreeService } from 'mo/services';
11+
import {
12+
builtInExplorerEditorPanel,
13+
EDITOR_MENU_CLOSE,
14+
EDITOR_MENU_CLOSE_ALL,
15+
EDITOR_MENU_CLOSE_OTHERS,
16+
EDITOR_MENU_CLOSE_SAVED,
17+
} from 'mo/model';
18+
import {
19+
EditorTree,
20+
IOpenEditProps,
21+
} from 'mo/workbench/sidebar/explore/editorTree';
22+
import { connect } from 'mo/react';
23+
import { IMenuItemProps, ITabProps } from 'mo/components';
24+
25+
export interface IEditorTreeController {
26+
readonly onClose: (tabId: string, groupId: number) => void;
27+
readonly onSelect: (tabId: string, groupId: number) => void;
28+
readonly onCloseGroup: (groupId: number) => void;
29+
readonly onSaveGroup: (groupId: number) => void;
30+
/**
31+
* Trigger by context menu click event
32+
* When click the context menu from group header, it doesn't have file info
33+
*/
34+
readonly onContextMenu: (
35+
menu: IMenuItemProps,
36+
groupId: number,
37+
file?: ITabProps
38+
) => void;
39+
}
40+
41+
@singleton()
42+
export class EditorTreeController
43+
extends Controller
44+
implements IEditorTreeController {
45+
private readonly explorerService: ExplorerService;
46+
private readonly folderTreeService: FolderTreeService;
47+
private readonly editService: EditorService;
48+
49+
constructor() {
50+
super();
51+
this.editService = container.resolve(EditorService);
52+
this.explorerService = container.resolve(ExplorerService);
53+
this.folderTreeService = container.resolve(FolderTreeService);
54+
this.initView();
55+
}
56+
57+
public initView() {
58+
const EditorTreeView = connect<IOpenEditProps>(
59+
this.editService,
60+
EditorTree
61+
);
62+
const { groupToolbar, ...restEditor } = builtInExplorerEditorPanel();
63+
const contextMenu = builtInEditorTreeContextMenu();
64+
const headerContextMenu = builtInEditorTreeHeaderContextMenu();
65+
66+
this.explorerService.addPanel({
67+
...restEditor,
68+
renderPanel: () => (
69+
<EditorTreeView
70+
contextMenu={contextMenu}
71+
headerContextMenu={headerContextMenu}
72+
groupToolbar={groupToolbar}
73+
onClose={this.onClose}
74+
onSelect={this.onSelect}
75+
onCloseGroup={this.onCloseGroup}
76+
onSaveGroup={this.onSaveGroup}
77+
onContextMenu={this.onContextMenu}
78+
getFileIconByExtensionName={
79+
this.folderTreeService.getFileIconByExtensionName
80+
}
81+
/>
82+
),
83+
});
84+
}
85+
86+
public onContextMenu = (
87+
menu: IMenuItemProps,
88+
groupId: number,
89+
file?: ITabProps
90+
) => {
91+
switch (menu.id) {
92+
case EDITOR_MENU_CLOSE:
93+
this.onClose(file?.id!, groupId);
94+
break;
95+
96+
case EDITOR_MENU_CLOSE_OTHERS:
97+
this.emit(EditorTreeEvent.onCloseOthers, file, groupId);
98+
break;
99+
100+
case EDITOR_MENU_CLOSE_SAVED:
101+
this.emit(EditorTreeEvent.onCloseSaved, groupId);
102+
break;
103+
104+
case EDITOR_MENU_CLOSE_ALL:
105+
this.emit(EditorTreeEvent.onCloseAll, groupId);
106+
break;
107+
108+
default:
109+
this.emit(EditorTreeEvent.onContextMenu, menu, file, groupId);
110+
break;
111+
}
112+
};
113+
114+
public onClose = (tabId: string, groupId: number) => {
115+
this.emit(EditorTreeEvent.onClose, tabId, groupId);
116+
};
117+
118+
public onSelect = (tabId: string, groupId: number) => {
119+
this.emit(EditorTreeEvent.onSelect, tabId, groupId);
120+
};
121+
122+
public onCloseGroup = (groupId: number) => {
123+
this.emit(EditorTreeEvent.onCloseAll, groupId);
124+
};
125+
126+
public onSaveGroup = (groupId: number) => {
127+
this.emit(EditorTreeEvent.onSaveAll, groupId);
128+
};
129+
}
130+
131+
// Register singleton
132+
container.resolve(EditorTreeController);

src/controller/explorer/explorer.tsx

+16-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import { IActivityBarItem } from 'mo/model/workbench/activityBar';
1010
import {
1111
builtInExplorerActivityItem,
1212
builtInExplorerFolderPanel,
13-
builtInExplorerEditorPanel,
1413
ExplorerEvent,
14+
EXPLORER_TOGGLE_CLOSE_ALL_EDITORS,
15+
EXPLORER_TOGGLE_SAVE_ALL,
16+
EXPLORER_TOGGLE_VERTICAL,
1517
IExplorerPanelItem,
1618
} from 'mo/model/workbench/explorer/explorer';
1719
import {
@@ -21,6 +23,7 @@ import {
2123
REMOVE_COMMAND_ID,
2224
FileTypes,
2325
FolderTreeEvent,
26+
EditorTreeEvent,
2427
} from 'mo/model';
2528
import { IActionBarItemProps } from 'mo/components/actionBar';
2629
import {
@@ -36,6 +39,7 @@ import {
3639
IMenuBarService,
3740
} from 'mo/services';
3841
import { FolderTreeController, IFolderTreeController } from './folderTree';
42+
3943
export interface IExplorerController {
4044
onActionsContextMenuClick?: (
4145
e: React.MouseEvent,
@@ -122,11 +126,6 @@ export class ExplorerController
122126
...builtInExplorerFolderPanel(),
123127
renderPanel: this.renderFolderTree,
124128
});
125-
126-
// add editor panel
127-
this.explorerService.addPanel({
128-
...builtInExplorerEditorPanel(),
129-
});
130129
}
131130

132131
private createFileOrFolder = (type: keyof typeof FileTypes) => {
@@ -177,6 +176,17 @@ export class ExplorerController
177176
this.emit(ExplorerEvent.onDeletePanel, parentPanel);
178177
break;
179178
}
179+
case EXPLORER_TOGGLE_CLOSE_ALL_EDITORS: {
180+
this.emit(EditorTreeEvent.onCloseAll);
181+
break;
182+
}
183+
case EXPLORER_TOGGLE_SAVE_ALL: {
184+
this.emit(EditorTreeEvent.onSaveAll);
185+
break;
186+
}
187+
case EXPLORER_TOGGLE_VERTICAL: {
188+
this.emit(EditorTreeEvent.onSplitEditorLayout);
189+
}
180190
default:
181191
console.log('onCollapseToolbar');
182192
}

src/controller/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ export * from './statusBar';
1010
export * from './workbench';
1111
export * from './explorer/explorer';
1212
export * from './explorer/folderTree';
13+
export * from './explorer/editorTree';
1314
export * from './explorer/outline';
1415
export * from './search/search';

src/extensions/editorTree/index.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import molecule from 'mo';
2+
import { IExtension } from 'mo/model/extension';
3+
4+
export const ExtendsEditorTree: IExtension = {
5+
activate() {
6+
molecule.editorTree.onSelect((tabId, groupId) => {
7+
molecule.editor.setActive(groupId, tabId);
8+
});
9+
10+
molecule.editorTree.onClose((tabId, groupId) => {
11+
molecule.editor.closeTab(tabId, groupId);
12+
});
13+
14+
molecule.editorTree.onCloseOthers((tabItem, groupId) => {
15+
molecule.editor.closeOthers(tabItem, groupId);
16+
});
17+
18+
molecule.editorTree.onCloseSaved((groupId) => {
19+
// TODO: editor close saved
20+
});
21+
22+
molecule.editorTree.onCloseAll((groupId) => {
23+
if (groupId) {
24+
molecule.editor.closeAll(groupId);
25+
} else {
26+
const { groups } = molecule.editor.getState();
27+
groups?.forEach((group) => {
28+
molecule.editor.closeAll(group.id!);
29+
});
30+
}
31+
});
32+
33+
molecule.editorTree.onSaveAll((groupId) => {
34+
// TODO: editor save
35+
});
36+
37+
molecule.editorTree.onLayout(() => {
38+
// TODO: layoutService
39+
});
40+
},
41+
};

src/extensions/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ExtendsMenuBar } from './menuBar';
66
import { ExtendsActivityBar } from './activityBar';
77
import { ExtendsPanel } from './panel';
88
import { ExtendsExplorer } from './explorer';
9+
import { ExtendsEditorTree } from './editorTree';
910

1011
import { defaultColorThemeExtension } from './theme-defaults';
1112
import { monokaiColorThemeExtension } from './theme-monokai';
@@ -22,6 +23,7 @@ export const defaultExtensions = [
2223
ExtendsStatusBar,
2324
ExtendsProblems,
2425
ExtendsExplorer,
26+
ExtendsEditorTree,
2527
defaultColorThemeExtension,
2628
monokaiColorThemeExtension,
2729
paleNightColorThemeExtension,

src/extensions/theme-defaults/themes/dark_defaults.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"editorGroupHeader.tabsBackground": "rgb(37, 37, 38)",
1313
"list.dropBackground": "#383B3D",
1414
"list.activeSelectionBackground": "#094771",
15+
"list.inactiveSelectionBackground": "#37373D",
1516
"list.focusOutline": "#007FD4",
1617
"activityBarBadge.background": "#007ACC",
1718
"sidebarTitle.foreground": "#BBBBBB",

src/extensions/theme-defaults/themes/light_defaults.json

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"sideBarSectionHeader.border": "#61616130",
1616
"list.hoverBackground": "#E8E8E8",
1717
"list.activeSelectionBackground": "#0060C0",
18+
"list.inactiveSelectionBackground": "#E4E6F1",
1819
"list.focusOutline": "#0090F1",
1920
"input.placeholderForeground": "#767676",
2021
"inputOption.activeBackground": "#007fd466",

src/i18n/source/en.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default {
2727
'sidebar.explore.title': 'Explorer',
2828
'sidebar.explore.folders': 'Folders',
2929
'sidebar.explore.openEditor': 'Open Editors',
30+
'sidebar.explore.openEditor.group': 'Group ${i}',
3031
'sidebar.explore.outline': 'Outline',
3132
'sidebar.search.title': 'Search',
3233
'sidebar.replace.placement': 'Replace',
@@ -53,6 +54,7 @@ export default {
5354
'editor.closeToRight': 'Close To Right',
5455
'editor.closeToLeft': 'Close To Left',
5556
'editor.closeAll': 'Close All',
57+
'editor.closeSaved': 'Close Saved',
5658
'editor.closeOthers': 'Close Others',
5759
'editor.close': 'Close',
5860
'editor.showOpenEditors': 'Show Opened Editors',

src/i18n/source/zh-CN.json

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"menu.about": "关于",
3333
"sidebar.explore.title": "浏览",
3434
"sidebar.explore.openEditor": "打开的编辑器",
35+
"sidebar.explore.openEditor.group": "第 ${i} 组",
3536
"sidebar.explore.outline": "轮廓",
3637
"sidebar.explore.outlineMore": "更多操作...",
3738
"sidebar.explore.refresh": "刷新浏览",
@@ -52,6 +53,7 @@
5253
"editor.closeToLeft": "关闭左边",
5354
"editor.closeAll": "关闭所有",
5455
"editor.closeOthers": "关闭其他",
56+
"editor.closeSaved": "关闭已保存",
5557
"editor.close": "关闭",
5658
"editor.showOpenEditors": "展示已打开的编辑器"
5759
}

src/model/workbench/editor.ts

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const EDITOR_MENU_CLOSE_TO_RIGHT = 'editor.closeToRight';
5252
export const EDITOR_MENU_CLOSE_TO_LEFT = 'editor.closeToLeft';
5353
export const EDITOR_MENU_CLOSE_ALL = 'editor.closeAll';
5454
export const EDITOR_MENU_CLOSE_OTHERS = 'editor.closeOthers';
55+
export const EDITOR_MENU_CLOSE_SAVED = 'editor.closeSaved';
5556
export const EDITOR_MENU_CLOSE = 'editor.close';
5657
export const EDITOR_MENU_SHOW_OPENEDITORS = 'editor.showOpenEditors';
5758

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { localize } from 'mo/i18n/localize';
2+
import {
3+
EDITOR_MENU_CLOSE,
4+
EDITOR_MENU_CLOSE_ALL,
5+
EDITOR_MENU_CLOSE_OTHERS,
6+
EDITOR_MENU_CLOSE_SAVED,
7+
} from 'mo/model';
8+
9+
export enum EditorTreeEvent {
10+
onClose = 'editorTree.close',
11+
onSelect = 'editorTree.select',
12+
onCloseOthers = 'editorTree.closeOthers',
13+
onCloseSaved = 'editorTree.closeSaved',
14+
onCloseAll = 'editorTree.closeAll',
15+
onSaveAll = 'editorTree.saveAll',
16+
onSplitEditorLayout = 'editorTree.splitEditorLayout',
17+
onContextMenu = 'editorTree.contextMenuClick',
18+
}
19+
20+
export function builtInEditorTreeHeaderContextMenu() {
21+
return [
22+
{
23+
id: EDITOR_MENU_CLOSE_SAVED,
24+
name: localize(EDITOR_MENU_CLOSE_SAVED, 'Close Saved'),
25+
},
26+
{
27+
id: EDITOR_MENU_CLOSE_ALL,
28+
name: localize(EDITOR_MENU_CLOSE_ALL, 'Close All'),
29+
},
30+
];
31+
}
32+
33+
export function builtInEditorTreeContextMenu() {
34+
return [
35+
{
36+
id: EDITOR_MENU_CLOSE,
37+
name: localize(EDITOR_MENU_CLOSE, 'Close'),
38+
},
39+
{
40+
id: EDITOR_MENU_CLOSE_OTHERS,
41+
name: localize(EDITOR_MENU_CLOSE_OTHERS, 'Close Others'),
42+
},
43+
{
44+
id: EDITOR_MENU_CLOSE_SAVED,
45+
name: localize(EDITOR_MENU_CLOSE_SAVED, 'Close Saved'),
46+
},
47+
{
48+
id: EDITOR_MENU_CLOSE_ALL,
49+
name: localize(EDITOR_MENU_CLOSE_ALL, 'Close All'),
50+
},
51+
];
52+
}
53+
54+
export class EditorTree {
55+
constructor() {}
56+
}

0 commit comments

Comments
 (0)