mirror of https://git.tuxpa.in/a/code-server.git
Fix some open file/folder issues
- "Open folder" now says "open folder" instead of "open file" - "Open folder" won't allow you to open files - "Open file" won't allow you to open directories Fixes #249.
This commit is contained in:
parent
2785e2219a
commit
7047be859c
|
@ -200,7 +200,13 @@ const bold = (text: string | number): string | number => {
|
||||||
const webpackConfig = require(path.resolve(__dirname, "..", "..", "web", "webpack.config.js"));
|
const webpackConfig = require(path.resolve(__dirname, "..", "..", "web", "webpack.config.js"));
|
||||||
const compiler = require("webpack")(webpackConfig);
|
const compiler = require("webpack")(webpackConfig);
|
||||||
app.use(require("webpack-dev-middleware")(compiler, {
|
app.use(require("webpack-dev-middleware")(compiler, {
|
||||||
logger,
|
logger: {
|
||||||
|
trace: (m: string): void => logger.trace("webpack", field("message", m)),
|
||||||
|
debug: (m: string): void => logger.debug("webpack", field("message", m)),
|
||||||
|
info: (m: string): void => logger.info("webpack", field("message", m)),
|
||||||
|
warn: (m: string): void => logger.warn("webpack", field("message", m)),
|
||||||
|
error: (m: string): void => logger.error("webpack", field("message", m)),
|
||||||
|
},
|
||||||
publicPath: webpackConfig.output.publicPath,
|
publicPath: webpackConfig.output.publicPath,
|
||||||
stats: webpackConfig.stats,
|
stats: webpackConfig.stats,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
--primary: #2A2E37;
|
--primary: #2A2E37;
|
||||||
--border: black;
|
--border: black;
|
||||||
--faded: #a0a1a5;
|
--faded: #a0a1a5;
|
||||||
|
--disabled: #888;
|
||||||
--header-background: #161616;
|
--header-background: #161616;
|
||||||
--header-foreground: white;
|
--header-foreground: white;
|
||||||
--list-active-selection-background: rgb(0, 120, 160);
|
--list-active-selection-background: rgb(0, 120, 160);
|
||||||
|
@ -101,6 +102,12 @@
|
||||||
background-color: var(--list-active-selection-background);
|
background-color: var(--list-active-selection-background);
|
||||||
color: var(--list-active-selection-foreground);
|
color: var(--list-active-selection-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled, &.disabled:hover {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: var(--disabled);
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +141,11 @@
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button[disabled], button[disabled]:hover {
|
||||||
|
color: var(--disabled);
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@ import { IThemeService } from "vs/platform/theme/common/themeService";
|
||||||
import { workbench } from "./workbench";
|
import { workbench } from "./workbench";
|
||||||
import "./dialog.scss";
|
import "./dialog.scss";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the type of dialog to show.
|
||||||
|
*/
|
||||||
export enum DialogType {
|
export enum DialogType {
|
||||||
NewFolder,
|
NewFolder,
|
||||||
Save,
|
Save,
|
||||||
|
@ -68,8 +71,12 @@ interface DialogEntry {
|
||||||
readonly isDirectory: boolean;
|
readonly isDirectory: boolean;
|
||||||
readonly size: number;
|
readonly size: number;
|
||||||
readonly lastModified: string;
|
readonly lastModified: string;
|
||||||
|
readonly isDisabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open and save dialogs.
|
||||||
|
*/
|
||||||
class Dialog {
|
class Dialog {
|
||||||
private _path: string | undefined;
|
private _path: string | undefined;
|
||||||
|
|
||||||
|
@ -265,8 +272,7 @@ class Dialog {
|
||||||
}
|
}
|
||||||
if (element.isDirectory) {
|
if (element.isDirectory) {
|
||||||
this.path = element.fullPath;
|
this.path = element.fullPath;
|
||||||
} else {
|
} else if (!(this.options as OpenDialogOptions).properties.openDirectory) {
|
||||||
// Open
|
|
||||||
this.selectEmitter.emit(element.fullPath);
|
this.selectEmitter.emit(element.fullPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -282,12 +288,18 @@ class Dialog {
|
||||||
});
|
});
|
||||||
buttonsNode.appendChild(cancelBtn);
|
buttonsNode.appendChild(cancelBtn);
|
||||||
const confirmBtn = document.createElement("button");
|
const confirmBtn = document.createElement("button");
|
||||||
confirmBtn.innerText = "Confirm";
|
const openFile = (this.options as OpenDialogOptions).properties.openFile;
|
||||||
|
confirmBtn.innerText = openFile ? "Open" : "Confirm";
|
||||||
confirmBtn.addEventListener("click", () => {
|
confirmBtn.addEventListener("click", () => {
|
||||||
if (this._path) {
|
if (this._path && !openFile) {
|
||||||
this.selectEmitter.emit(this._path);
|
this.selectEmitter.emit(this._path);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Since a single click opens a file, the only time this button can be
|
||||||
|
// used is on a directory, which is invalid for opening files.
|
||||||
|
if (openFile) {
|
||||||
|
confirmBtn.disabled = true;
|
||||||
|
}
|
||||||
buttonsNode.appendChild(confirmBtn);
|
buttonsNode.appendChild(confirmBtn);
|
||||||
this.root.appendChild(buttonsNode);
|
this.root.appendChild(buttonsNode);
|
||||||
this.entryList.layout();
|
this.entryList.layout();
|
||||||
|
@ -303,6 +315,9 @@ class Dialog {
|
||||||
return this.errorEmitter.event;
|
return this.errorEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the dialog.
|
||||||
|
*/
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.selectEmitter.dispose();
|
this.selectEmitter.dispose();
|
||||||
this.errorEmitter.dispose();
|
this.errorEmitter.dispose();
|
||||||
|
@ -310,6 +325,9 @@ class Dialog {
|
||||||
this.background.remove();
|
this.background.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build and insert the path shown at the top of the dialog.
|
||||||
|
*/
|
||||||
private buildPath(): void {
|
private buildPath(): void {
|
||||||
while (this.pathNode.lastChild) {
|
while (this.pathNode.lastChild) {
|
||||||
this.pathNode.removeChild(this.pathNode.lastChild);
|
this.pathNode.removeChild(this.pathNode.lastChild);
|
||||||
|
@ -376,6 +394,9 @@ class Dialog {
|
||||||
return (<any>this.entryList).typeFilterController.filter._pattern;
|
return (<any>this.entryList).typeFilterController.filter._pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the files and return dialog entries.
|
||||||
|
*/
|
||||||
private async list(directory: string): Promise<ReadonlyArray<DialogEntry>> {
|
private async list(directory: string): Promise<ReadonlyArray<DialogEntry>> {
|
||||||
const paths = (await util.promisify(fs.readdir)(directory)).sort();
|
const paths = (await util.promisify(fs.readdir)(directory)).sort();
|
||||||
const stats = await Promise.all(paths.map(p => util.promisify(fs.stat)(path.join(directory, p))));
|
const stats = await Promise.all(paths.map(p => util.promisify(fs.stat)(path.join(directory, p))));
|
||||||
|
@ -386,6 +407,8 @@ class Dialog {
|
||||||
isDirectory: stat.isDirectory(),
|
isDirectory: stat.isDirectory(),
|
||||||
lastModified: stat.mtime.toDateString(),
|
lastModified: stat.mtime.toDateString(),
|
||||||
size: stat.size,
|
size: stat.size,
|
||||||
|
// If we are opening a directory, show files as disabled.
|
||||||
|
isDisabled: !stat.isDirectory() && (this.options as OpenDialogOptions).properties.openDirectory,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,11 +420,17 @@ interface DialogEntryData {
|
||||||
label: HighlightedLabel;
|
label: HighlightedLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering for the different parts of a dialog entry.
|
||||||
|
*/
|
||||||
class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEntryData> {
|
class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEntryData> {
|
||||||
public get templateId(): string {
|
public get templateId(): string {
|
||||||
return "dialog-entry";
|
return "dialog-entry";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append and return containers for each part of the dialog entry.
|
||||||
|
*/
|
||||||
public renderTemplate(container: HTMLElement): DialogEntryData {
|
public renderTemplate(container: HTMLElement): DialogEntryData {
|
||||||
addClass(container, "dialog-entry");
|
addClass(container, "dialog-entry");
|
||||||
addClass(container, "dialog-grid");
|
addClass(container, "dialog-grid");
|
||||||
|
@ -422,6 +451,9 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a dialog entry.
|
||||||
|
*/
|
||||||
public renderElement(node: ITreeNode<DialogEntry, string>, _index: number, templateData: DialogEntryData): void {
|
public renderElement(node: ITreeNode<DialogEntry, string>, _index: number, templateData: DialogEntryData): void {
|
||||||
templateData.icon.className = "dialog-entry-icon monaco-icon-label";
|
templateData.icon.className = "dialog-entry-icon monaco-icon-label";
|
||||||
const classes = getIconClasses(
|
const classes = getIconClasses(
|
||||||
|
@ -444,8 +476,19 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||||
}] : []);
|
}] : []);
|
||||||
templateData.size.innerText = node.element.size.toString();
|
templateData.size.innerText = node.element.size.toString();
|
||||||
templateData.lastModified.innerText = node.element.lastModified;
|
templateData.lastModified.innerText = node.element.lastModified;
|
||||||
|
|
||||||
|
// We know this exists because we created the template.
|
||||||
|
const entryContainer = templateData.label.element.parentElement!.parentElement!.parentElement!;
|
||||||
|
if (node.element.isDisabled) {
|
||||||
|
entryContainer.classList.add("disabled");
|
||||||
|
} else {
|
||||||
|
entryContainer.classList.remove("disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing (not implemented).
|
||||||
|
*/
|
||||||
public disposeTemplate(_templateData: DialogEntryData): void {
|
public disposeTemplate(_templateData: DialogEntryData): void {
|
||||||
// throw new Error("Method not implemented.");
|
// throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,9 @@ export class WindowsService implements IWindowsService {
|
||||||
private readonly window = new electron.BrowserWindow();
|
private readonly window = new electron.BrowserWindow();
|
||||||
|
|
||||||
// Dialogs
|
// Dialogs
|
||||||
public async pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
public async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||||
showOpenDialog({
|
showOpenDialog({
|
||||||
...(_options.dialogOptions || {}),
|
...(options.dialogOptions || {}),
|
||||||
properties: {
|
properties: {
|
||||||
openFile: true,
|
openFile: true,
|
||||||
openDirectory: true,
|
openDirectory: true,
|
||||||
|
@ -66,9 +66,9 @@ export class WindowsService implements IWindowsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pickFileAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
public async pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||||
showOpenDialog({
|
showOpenDialog({
|
||||||
...(_options.dialogOptions || {}),
|
...(options.dialogOptions || {}),
|
||||||
properties: {
|
properties: {
|
||||||
openFile: true,
|
openFile: true,
|
||||||
},
|
},
|
||||||
|
@ -84,9 +84,15 @@ export class WindowsService implements IWindowsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
public async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||||
|
if (!options.dialogOptions) {
|
||||||
|
options.dialogOptions = {};
|
||||||
|
}
|
||||||
|
if (!options.dialogOptions.title) {
|
||||||
|
options.dialogOptions.title = "Open Folder";
|
||||||
|
}
|
||||||
showOpenDialog({
|
showOpenDialog({
|
||||||
...(_options.dialogOptions || {}),
|
...(options.dialogOptions || {}),
|
||||||
properties: {
|
properties: {
|
||||||
openDirectory: true,
|
openDirectory: true,
|
||||||
},
|
},
|
||||||
|
@ -97,9 +103,9 @@ export class WindowsService implements IWindowsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
public async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||||
showOpenDialog({
|
showOpenDialog({
|
||||||
...(_options.dialogOptions || {}),
|
...(options.dialogOptions || {}),
|
||||||
properties: {
|
properties: {
|
||||||
openDirectory: true,
|
openDirectory: true,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue