137 lines
5.5 KiB
TypeScript
137 lines
5.5 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
import { generateTkinterCode } from './generator';
|
|
import { parsePythonToGrapes } from './pythonParser';
|
|
|
|
export class TkinterEditorProvider implements vscode.CustomTextEditorProvider {
|
|
|
|
public static readonly viewType = 'tkinter-designer.editor';
|
|
|
|
public static register(context: vscode.ExtensionContext): vscode.Disposable {
|
|
const provider = new TkinterEditorProvider(context);
|
|
return vscode.window.registerCustomEditorProvider(TkinterEditorProvider.viewType, provider);
|
|
}
|
|
|
|
constructor(
|
|
private readonly context: vscode.ExtensionContext
|
|
) { }
|
|
|
|
private isSaving = false;
|
|
|
|
public async resolveCustomTextEditor(
|
|
document: vscode.TextDocument,
|
|
webviewPanel: vscode.WebviewPanel,
|
|
_token: vscode.CancellationToken
|
|
): Promise<void> {
|
|
|
|
webviewPanel.webview.options = {
|
|
enableScripts: true,
|
|
localResourceRoots: [vscode.Uri.file(path.join(this.context.extensionPath, 'media'))]
|
|
};
|
|
|
|
webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview);
|
|
|
|
webviewPanel.webview.onDidReceiveMessage(e => {
|
|
switch (e.type) {
|
|
case 'request-load':
|
|
this.handleRequestLoad(document, webviewPanel);
|
|
break;
|
|
case 'update-code':
|
|
this.isSaving = true;
|
|
this.handleSave(document, e.payload);
|
|
setTimeout(() => { this.isSaving = false; }, 500);
|
|
break;
|
|
case 'request-import':
|
|
this.handleImport(webviewPanel);
|
|
break;
|
|
}
|
|
});
|
|
|
|
const folderPath = path.dirname(document.uri.fsPath);
|
|
const fileNameBase = path.basename(document.uri.fsPath, path.extname(document.uri.fsPath));
|
|
const pyFilePath = path.join(folderPath, `${fileNameBase}.py`);
|
|
|
|
const watcher = vscode.workspace.createFileSystemWatcher(pyFilePath);
|
|
|
|
watcher.onDidChange(() => {
|
|
//
|
|
});
|
|
|
|
webviewPanel.onDidDispose(() => watcher.dispose());
|
|
}
|
|
|
|
private handleRequestLoad(document: vscode.TextDocument, panel: vscode.WebviewPanel) {
|
|
const text = document.getText();
|
|
let payload = {};
|
|
try {
|
|
if (text.trim().length > 0) payload = JSON.parse(text);
|
|
} catch (e) { console.error(e); }
|
|
panel.webview.postMessage({ type: 'load-data', payload: payload });
|
|
}
|
|
|
|
private async handleSave(document: vscode.TextDocument, jsonPayload: any) {
|
|
const folderPath = path.dirname(document.uri.fsPath);
|
|
const fileNameBase = path.basename(document.uri.fsPath, path.extname(document.uri.fsPath));
|
|
const pyFilePath = path.join(folderPath, `${fileNameBase}.py`);
|
|
|
|
const pythonCode = generateTkinterCode(jsonPayload);
|
|
try {
|
|
fs.writeFileSync(pyFilePath, pythonCode);
|
|
} catch (e) {
|
|
console.error(`Python Save error: ${e}`);
|
|
}
|
|
|
|
// СОХРАНЕНИЕ JSON
|
|
const edit = new vscode.WorkspaceEdit();
|
|
const fullRange = new vscode.Range(
|
|
document.positionAt(0),
|
|
document.positionAt(document.getText().length)
|
|
);
|
|
edit.replace(document.uri, fullRange, JSON.stringify(jsonPayload, null, 2));
|
|
await vscode.workspace.applyEdit(edit);
|
|
await document.save();
|
|
}
|
|
|
|
private async handleImport(panel: vscode.WebviewPanel) {
|
|
const uris = await vscode.window.showOpenDialog({
|
|
canSelectMany: false,
|
|
openLabel: 'Import Python',
|
|
filters: { 'Python Files': ['py'] }
|
|
});
|
|
if (uris && uris[0]) {
|
|
const content = fs.readFileSync(uris[0].fsPath, 'utf-8');
|
|
const data = parsePythonToGrapes(content);
|
|
panel.webview.postMessage({ type: 'import-data', payload: data });
|
|
}
|
|
}
|
|
|
|
private getHtmlForWebview(webview: vscode.Webview): string {
|
|
const mediaPath = path.join(this.context.extensionPath, 'media');
|
|
const scriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'grapes.min.js')));
|
|
const styleUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'grapes.min.css')));
|
|
const mainScriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'main.js')));
|
|
const fontUrl = "https://unpkg.com/grapesjs/dist/fonts/grapes.woff";
|
|
|
|
return `<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline' https:; script-src ${webview.cspSource} 'unsafe-inline' 'unsafe-eval'; font-src ${webview.cspSource} https: data:;">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link href="${styleUri}" rel="stylesheet">
|
|
<style>
|
|
html, body { height: 100%; margin: 0; overflow: hidden; }
|
|
#editor { height: 100%; }
|
|
@font-face { font-family: 'GrapesJS'; src: url('${fontUrl}') format('woff'); font-weight: normal; font-style: normal; }
|
|
</style>
|
|
<title>Tkinter Designer</title>
|
|
</head>
|
|
<body>
|
|
<div id="editor"></div>
|
|
<script src="${scriptUri}"></script>
|
|
<script src="${mainScriptUri}"></script>
|
|
</body>
|
|
</html>`;
|
|
}
|
|
} |