Files
mixly3/common/modules/mixly-modules/common/app.js

683 lines
21 KiB
JavaScript

goog.loadJs('common', () => {
goog.require('path');
goog.require('layui');
goog.require('Mixly.Url');
goog.require('Mixly.Config');
goog.require('Mixly.Env');
goog.require('Mixly.Msg');
goog.require('Mixly.Drag');
goog.require('Mixly.Nav');
goog.require('Mixly.Workspace');
goog.require('Mixly.FooterBar');
goog.require('Mixly.HTMLTemplate');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Debug');
goog.require('Mixly.Component');
goog.require('Mixly.EditorMix');
goog.require('Mixly.Electron.Loader');
goog.require('Mixly.Electron.FS');
goog.require('Mixly.Electron.File');
goog.require('Mixly.Electron.LibManager');
goog.require('Mixly.Electron.Serial');
goog.require('Mixly.Electron.ArduShell');
goog.require('Mixly.Electron.BU');
goog.require('Mixly.Electron.PythonShell');
goog.require('Mixly.Web.BU');
goog.require('Mixly.Web.FS');
goog.require('Mixly.Web.File');
goog.require('Mixly.Web.Serial');
goog.require('Mixly.WebSocket.File');
goog.require('Mixly.WebSocket.Serial');
goog.require('Mixly.WebSocket.ArduShell');
goog.require('Mixly.WebSocket.BU');
goog.provide('Mixly.App');
const {
Url,
Config,
Env,
Msg,
Drag,
Nav,
Workspace,
FooterBar,
HTMLTemplate,
LayerExt,
Debug,
Component,
EditorMix,
Electron = {},
Web = {},
WebSocket = {}
} = Mixly;
const { Loader } = Electron;
let currentObj = null;
if (goog.isElectron) {
currentObj = Electron;
} else {
if (Env.hasSocketServer) {
currentObj = WebSocket;
} else {
currentObj = Web;
}
}
const {
FS,
File,
LibManager,
ArduShell,
BU,
PythonShell,
Serial
} = currentObj;
const { BOARD, SELECTED_BOARD } = Config;
const { layer } = layui;
const electron = Mixly.require('electron');
class App extends Component {
static {
HTMLTemplate.add(
'html/app.html',
new HTMLTemplate(goog.get(path.join(Env.templatePath, 'html/app.html')))
);
}
#resizeObserver_ = null;
#workspace_ = null;
#nav_ = null;
#footerbar_ = null;
constructor(element) {
super();
const $content = $(HTMLTemplate.get('html/app.html').render());
this.setContent($content);
this.mountOn($(element));
this.#nav_ = new Nav();
this.#nav_.mountOn($content.find('.mixly-nav'));
this.#workspace_ = new Workspace($content.find('.mixly-workspace')[0]);
this.#workspace_.getEditorsManager().getTabs().addTab({
name: 'Untitled-1.mix',
title: 'Untitled-1.mix',
type: '.mix',
favicon: 'fileicon-mix'
});
this.#footerbar_ = new FooterBar();
this.#footerbar_.mountOn($content.find('.mixly-footerbar'));
this.#addEventsListenerForNav_();
this.#addEventsListenerForWorkspace_();
this.#addObserver_();
Mixly.mainStatusBarTabs = this.#workspace_.getStatusBarsManager();
Serial.refreshPorts();
if (goog.isElectron) {
PythonShell.init();
}
}
#addEventsListenerForNav_() {
const editorsManager = this.#workspace_.getEditorsManager();
this.#nav_.register({
id: 'home-btn',
preconditionFn: () => {
return true;
},
callback: () => {
this.#onbeforeunload_();
},
scopeType: Nav.Scope.LEFT,
weight: -1
});
this.#nav_.register({
icon: 'icon-ccw',
title: 'undo(ctrl+z)',
id: 'undo-btn',
displayText: Msg.Lang['nav.btn.undo'],
preconditionFn: () => {
return !!editorsManager.getActive();
},
callback: () => editorsManager.getActive().undo(),
scopeType: Nav.Scope.LEFT,
weight: 0
});
this.#nav_.register({
icon: 'icon-cw',
title: 'redo(ctrl+y)',
id: 'redo-btn',
displayText: Msg.Lang['nav.btn.redo'],
preconditionFn: () => {
return !!editorsManager.getActive();
},
callback: () => editorsManager.getActive().redo(),
scopeType: Nav.Scope.LEFT,
weight: 1
});
this.#nav_.register({
icon: 'icon-link',
title: '',
id: 'port-add-btn',
displayText: Msg.Lang['nav.btn.addDevice'],
preconditionFn: () => {
if (goog.isElectron || Env.hasSocketServer) {
return false;
}
return true;
},
callback: () => BU.requestPort().catch(Debug.error),
scopeType: Nav.Scope.LEFT,
weight: 3
});
this.#nav_.register({
icon: 'icon-check',
title: '',
id: 'arduino-compile-btn',
displayText: Msg.Lang['nav.btn.compile'],
preconditionFn: () => {
if (!goog.isElectron && !Env.hasSocketServer && !Env.hasCompiler) {
return false;
}
if (!SELECTED_BOARD?.nav?.compile || !SELECTED_BOARD?.nav?.upload) {
return false;
}
const workspace = Workspace.getMain();
const editorsManager = workspace.getEditorsManager();
const editor = editorsManager.getActive();
if (!editor) {
return false;
}
if (editor instanceof EditorMix) {
return true;
}
return false;
},
callback: () => ArduShell.initCompile(),
scopeType: Nav.Scope.LEFT,
weight: 4
});
this.#nav_.register({
icon: 'icon-upload',
title: '',
id: 'arduino-upload-btn',
displayText: Msg.Lang['nav.btn.upload'],
preconditionFn: () => {
if (!goog.isElectron && !Env.hasSocketServer && !Env.hasCompiler) {
return false;
}
if (!SELECTED_BOARD?.nav?.compile || !SELECTED_BOARD?.nav?.upload) {
return false;
}
const workspace = Workspace.getMain();
const editorsManager = workspace.getEditorsManager();
const editor = editorsManager.getActive();
if (!editor) {
return false;
}
if (editor instanceof EditorMix) {
return true;
}
return false;
},
callback: () => ArduShell.initUpload(),
scopeType: Nav.Scope.LEFT,
weight: 5
});
this.#nav_.register({
icon: 'icon-upload-1',
title: '',
id: 'command-burn-btn',
displayText: Msg.Lang['nav.btn.burn'],
preconditionFn: () => {
return SELECTED_BOARD?.nav?.burn;
},
callback: () => BU.initBurn(),
scopeType: Nav.Scope.LEFT,
weight: 3
});
this.#nav_.register({
icon: 'icon-upload',
title: '',
id: 'command-upload-btn',
displayText: Msg.Lang['nav.btn.upload'],
preconditionFn: () => {
return SELECTED_BOARD?.nav?.upload && !SELECTED_BOARD?.nav?.compile;
},
callback: () => BU.initUpload(),
scopeType: Nav.Scope.LEFT,
weight: 5
});
this.#nav_.register({
icon: 'icon-play-circled',
title: '',
id: 'python-run-btn',
displayText: Msg.Lang['nav.btn.run'],
preconditionFn: () => {
return goog.isElectron && SELECTED_BOARD?.nav?.run;
},
callback: () => PythonShell.run(),
scopeType: Nav.Scope.LEFT,
weight: 4
});
this.#nav_.register({
icon: 'icon-cancel',
title: '',
id: 'python-stop-btn',
displayText: Msg.Lang['nav.btn.stop'],
preconditionFn: () => {
return goog.isElectron && SELECTED_BOARD?.nav?.cancel;
},
callback: () => PythonShell.stop(),
scopeType: Nav.Scope.LEFT,
weight: 5
});
this.#nav_.register({
icon: 'icon-usb',
title: '',
id: 'serial-open-btn',
displayText: Msg.Lang['statusbar.serial.port'],
preconditionFn: () => {
return true;
},
callback: () => {
const statusBarsManager = this.#workspace_.getStatusBarsManager();
statusBarsManager.openSelectedPort();
},
scopeType: Nav.Scope.LEFT,
weight: 10
});
/*const leftSideBarOption = this.#nav_.register({
icon: 'codicon-layout-sidebar-left-off',
title: '操作左侧边栏',
id: 'left-sidebar-btn',
preconditionFn: () => {
return true;
},
callback: (element) => {
const $a = $(element).children('a');
const drag = this.#workspace_.dragVLeft;
if (drag.shown === Drag.Extend.NEGATIVE) {
drag.exitfull(Drag.Extend.NEGATIVE);
} else {
drag.full(Drag.Extend.NEGATIVE);
}
},
scopeType: Nav.Scope.CENTER,
weight: 1
});
const leftSideBarEvents = this.#workspace_.dragVLeft;
leftSideBarEvents.bind('onfull', (type) => {
const { $btn } = leftSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.NEGATIVE) {
return;
}
$a.removeClass('codicon-layout-sidebar-left');
$a.addClass('codicon-layout-sidebar-left-off');
});
leftSideBarEvents.bind('exitfull', (type) => {
const { $btn } = leftSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.NEGATIVE) {
return;
}
$a.removeClass('codicon-layout-sidebar-left-off');
$a.addClass('codicon-layout-sidebar-left');
});
const rightSideBarOption = this.#nav_.register({
icon: 'codicon-layout-sidebar-right-off',
title: '操作右侧边栏',
id: 'right-sidebar-btn',
preconditionFn: () => {
return true;
},
callback: (element) => {
const $a = $(element).children('a');
const drag = this.#workspace_.dragVRight;
if (drag.shown === Drag.Extend.POSITIVE) {
drag.exitfull(Drag.Extend.POSITIVE);
} else {
drag.full(Drag.Extend.POSITIVE);
}
},
scopeType: Nav.Scope.CENTER,
weight: 3
});
const rightSideBarEvents = this.#workspace_.dragVRight;
rightSideBarEvents.bind('onfull', (type) => {
const { $btn } = rightSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.POSITIVE) {
return;
}
$a.removeClass('codicon-layout-sidebar-right');
$a.addClass('codicon-layout-sidebar-right-off');
});
rightSideBarEvents.bind('exitfull', (type) => {
const { $btn } = rightSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.POSITIVE) {
return;
}
$a.removeClass('codicon-layout-sidebar-right-off');
$a.addClass('codicon-layout-sidebar-right');
});*/
const bottomSideBarOption = this.#nav_.register({
icon: 'codicon-layout-panel-off',
title: Msg.Lang['nav.btn.toggleStatusbar'],
id: 'bottom-sidebar-btn',
preconditionFn: () => {
return true;
},
callback: (element) => {
const $a = $(element).children('a');
const drag = this.#workspace_.dragH;
if (drag.shown === Drag.Extend.POSITIVE) {
drag.exitfull(Drag.Extend.POSITIVE);
} else {
drag.full(Drag.Extend.POSITIVE);
}
},
scopeType: Nav.Scope.CENTER,
weight: 2
});
const bottomSideBarEvents = this.#workspace_.dragH;
bottomSideBarEvents.bind('onfull', (type) => {
const { $btn } = bottomSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.POSITIVE) {
return;
}
$a.removeClass('codicon-layout-panel');
$a.addClass('codicon-layout-panel-off');
});
bottomSideBarEvents.bind('exitfull', (type) => {
const { $btn } = bottomSideBarOption;
const $a = $btn.children('a');
if (type !== Drag.Extend.POSITIVE) {
return;
}
$a.removeClass('codicon-layout-panel-off');
$a.addClass('codicon-layout-panel');
});
this.#nav_.register({
id: 'file',
displayText: Msg.Lang['nav.btn.file'],
preconditionFn: () => {
return true;
},
scopeType: Nav.Scope.RIGHT,
weight: 1
});
this.#nav_.register({
icon: 'icon-doc-new',
id: ['file', 'new-file'],
displayText: Msg.Lang['nav.btn.file.new'],
preconditionFn: () => {
return true;
},
callback: () => File.new(),
scopeType: Nav.Scope.RIGHT,
weight: 1
});
this.#nav_.register({
icon: 'icon-doc',
id: ['file', 'open-file'],
displayText: Msg.Lang['nav.btn.file.open'],
preconditionFn: () => {
return true;
},
callback: (elem) => File.open(),
scopeType: Nav.Scope.RIGHT,
weight: 2
});
this.#nav_.register({
id: ['file', 'hr'],
scopeType: Nav.Scope.RIGHT,
weight: 3
});
this.#nav_.register({
icon: 'icon-floppy',
id: ['file', 'save-file'],
displayText: Msg.Lang['nav.btn.file.save'],
preconditionFn: () => {
return true;
},
callback: (elem) => File.save(),
scopeType: Nav.Scope.RIGHT,
weight: 4
});
this.#nav_.register({
icon: 'icon-save-as',
id: ['file', 'save-as-file'],
displayText: Msg.Lang['nav.btn.file.saveAs'],
preconditionFn: () => {
return true;
},
callback: () => File.saveAs(),
scopeType: Nav.Scope.RIGHT,
weight: 5
});
this.#nav_.register({
id: ['file', 'hr'],
preconditionFn: () => {
return goog.isElectron && BOARD?.nav?.setting?.thirdPartyLibrary;
},
scopeType: Nav.Scope.RIGHT,
weight: 6
});
this.#nav_.register({
icon: 'icon-export',
id: ['file', 'export-file'],
displayText: Msg.Lang['nav.btn.file.exportAs'],
preconditionFn: () => {
return goog.isElectron && BOARD?.nav?.setting?.thirdPartyLibrary;
},
callback: (elem) => File.exportLib(),
scopeType: Nav.Scope.RIGHT,
weight: 7
});
this.#nav_.register({
id: 'setting',
displayText: Msg.Lang['nav.btn.setting'],
preconditionFn: () => {
return true;
},
scopeType: Nav.Scope.RIGHT,
weight: 1
});
this.#nav_.register({
icon: 'icon-menu',
id: ['setting', 'manage-libs'],
displayText: Msg.Lang['nav.btn.setting.manageLibs'],
preconditionFn: () => {
return goog.isElectron && BOARD?.nav?.setting?.thirdPartyLibrary;
},
callback: () => LibManager.showManageDialog(),
scopeType: Nav.Scope.RIGHT,
weight: 1
});
/*this.#nav_.register({
icon: 'icon-upload-1',
id: ['setting', 'firmware'],
displayText: Msg.Lang['nav.btn.setting.firmware'],
preconditionFn: () => {
if (goog.isElectron) {
return !!BOARD?.burn?.special;
} else {
return !!BOARD?.web?.burn?.special;
}
},
callback: () => BU.burnWithSpecialBin(),
scopeType: Nav.Scope.RIGHT,
weight: 2
});*/
this.#nav_.register({
icon: 'icon-comment-1',
id: ['setting', 'feedback'],
displayText: Msg.Lang['nav.btn.setting.feedback'],
preconditionFn: () => {
return true;
},
callback: (elem) => {
const href = 'https://gitee.com/mixly2/mixly2.0_src/issues';
Url.open(href);
},
scopeType: Nav.Scope.RIGHT,
weight: 2
});
}
#addEventsListenerForWorkspace_() {
const editorsManager = this.#workspace_.getEditorsManager();
const editorTabs = editorsManager.getTabs();
editorTabs.bind('tabCheckDestroy', (event) => {
const { tabEl } = event.detail;
const id = $(tabEl).attr('data-tab-id');
const editor = editorsManager.get(id);
if (!editor) {
return;
}
if (editor.isDirty()) {
layer.confirm(`是否保存对${path.basename(id)}的修改?`, {
title: false,
shade: LayerExt.SHADE_ALL,
resize: false,
btn: ['保存', '不保存', '取消'],
closeBtn: 1,
btn1: (index) => {
const $tab = editor.getTab();
if ($tab.attr('data-link-file') === 'true') {
FS.writeFile($tab.attr('data-tab-id'), editor.getValue())
.then(() => {
editor.removeDirty();
editorsManager.remove(id);
layer.close(index);
layer.msg('已保存文件');
})
.catch(Debug.error);
} else {
FS.showSaveFilePicker(id, $tab.attr('data-tab-type'))
.then((filePath) => {
if (!filePath) {
return Promise.resolve(true);
}
return FS.writeFile(filePath, editor.getValue());
})
.then((status) => {
if (status) {
return;
}
editor.removeDirty();
editorsManager.remove(id);
layer.close(index);
layer.msg('已保存文件');
})
.catch(Debug.error);
}
},
btn2: (index) => {
editor.removeDirty();
editorsManager.remove(id);
layer.close(index);
},
btn3: (index) => {
layer.close(index);
},
success: (layero) => {
const { classList } = layero[0].childNodes[1].childNodes[0];
classList.remove('layui-layer-close2');
classList.add('layui-layer-close1');
}
});
}
return !editor.isDirty();
});
}
#addObserver_() {
this.#resizeObserver_ = new ResizeObserver((entries) => {
let contentRect = entries[0].contentRect;
if (!(contentRect.width || contentRect.height)) return;
this.resize();
});
this.#resizeObserver_.observe(this.getContent()[0]);
}
#onbeforeunload_() {
if (goog.isElectron) {
Loader.onbeforeunload();
} else {
let href = Config.pathPrefix + 'index.html?' + Url.jsonToUrl({ boardType: BOARD.boardType });
window.location.replace(href);
}
}
getNav() {
return this.#nav_;
}
getWorkspace() {
return this.#workspace_;
}
getFooterBar() {
return this.#footerbar_;
}
resize() {
this.#nav_.resize();
this.#workspace_.resize();
this.#footerbar_.resize();
}
removeSkeleton() {
const $appLoading = $('.mixly-app-loading');
$appLoading.remove();
}
dispose() {
this.#resizeObserver_.disconnect();
this.#workspace_.dispose();
super.dispose();
}
}
Mixly.App = App;
});