Further improve the remote font intergation: Better preload strategy, better loading and UI interation.

This commit is contained in:
NI
2019-12-13 14:35:23 +08:00
parent a5696d6b63
commit c018c7b4be
12 changed files with 223 additions and 78 deletions

View File

@@ -29,11 +29,13 @@ export class Result {
* @param {string} name Result type
* @param {Info} info Result info
* @param {object} control Result controller
* @param {string} ui User interfact this command will use
*/
constructor(name, info, control) {
constructor(name, info, control, ui) {
this.name = name;
this.info = info;
this.control = control;
this.ui = ui;
}
}
@@ -501,6 +503,16 @@ class Wizard {
return this.built.started();
}
/**
* Return the name of the control info of current wizard
*
* @returns {object}
*
*/
control() {
return this.built.control();
}
/**
* Close current wizard
*

View File

@@ -44,18 +44,17 @@ export class Controls {
* Get a control
*
* @param {string} type Type of the control
* @param {...any} data Data needed to build the control
*
* @returns {object} Control object
*
* @throws {Exception} When given control type is undefined
*
*/
get(type, ...data) {
get(type) {
if (typeof this.controls[type] !== "object") {
throw new Exception('Control "' + type + '" was undefined');
}
return this.controls[type].build(...data);
return this.controls[type];
}
}

View File

@@ -487,7 +487,7 @@ class Wizard {
credential: ""
};
this.step = subs;
this.controls = controls;
this.controls = controls.get("SSH");
this.history = history;
this.step.resolve(this.stepInitialPrompt());
@@ -497,6 +497,10 @@ class Wizard {
return this.hasStarted;
}
control() {
return this.controls;
}
close() {
this.step.resolve(
this.stepErrorDone(
@@ -603,7 +607,7 @@ class Wizard {
new command.Result(
configInput.user + "@" + configInput.host,
self.info,
self.controls.get("SSH", {
self.controls.build({
charset: configInput.charset,
send(data) {
return commandHandler.sendData(data);
@@ -615,7 +619,8 @@ class Wizard {
return commandHandler.sendResize(rows, cols);
},
events: commandHandler.events
})
}),
self.controls.ui()
)
)
);

View File

@@ -249,7 +249,7 @@ class Wizard {
this.config = config;
this.session = session;
this.step = subs;
this.controls = controls;
this.controls = controls.get("Telnet");
this.history = history;
this.step.resolve(this.stepInitialPrompt());
@@ -259,6 +259,10 @@ class Wizard {
return this.hasStarted;
}
control() {
return this.controls;
}
close() {
this.step.resolve(
this.stepErrorDone(
@@ -337,7 +341,7 @@ class Wizard {
new command.Result(
configInput.host,
self.info,
self.controls.get("Telnet", {
self.controls.build({
charset: parsedConfig.charset,
send(data) {
return commandHandler.sendData(data);
@@ -346,7 +350,8 @@ class Wizard {
return commandHandler.sendClose();
},
events: commandHandler.events
})
}),
self.controls.ui()
)
)
);

View File

@@ -100,10 +100,6 @@ class Control {
this.resizer(dim.rows, dim.cols);
}
ui() {
return "Console";
}
enabled() {
this.enable = true;
}
@@ -168,6 +164,10 @@ export class SSH {
return "SSH";
}
ui() {
return "Console";
}
build(data) {
return new Control(data, this.color);
}

View File

@@ -428,10 +428,6 @@ class Control {
this.parser.requestWindowResize();
}
ui() {
return "Console";
}
enabled() {
this.enable = true;
}
@@ -527,6 +523,10 @@ export class Telnet {
return "Telnet";
}
ui() {
return "Console";
}
build(data) {
return new Control(data, this.color);
}

View File

@@ -369,16 +369,6 @@
flex-direction: column;
color: #fff;
font-size: 1.2em;
position: relative;
}
#home-content-preload-drop {
height: 0px;
width: 0px;
overflow: hidden;
position: absolute;
bottom: 0;
right: 0;
}
@media (max-width: 768px) {

View File

@@ -104,8 +104,6 @@
>.
</p>
</div>
<div id="home-content-preload-drop"></div>
</screens>
<connect-widget
@@ -470,6 +468,7 @@ export default {
name: data.name,
info: data.info,
control: data.control,
ui: data.ui,
toolbar: false,
indicator: {
level: "",
@@ -534,22 +533,40 @@ export default {
}
},
tabWarning(index, msg) {
if (msg.length > 0) {
this.tab.tabs[index].indicator.message = msg;
this.tab.tabs[index].indicator.level = "warning";
} else {
if (msg.toDismiss) {
if (
this.tab.tabs[index].indicator.message !== msg.text ||
this.tab.tabs[index].indicator.level !== "warning"
) {
return;
}
this.tab.tabs[index].indicator.message = "";
this.tab.tabs[index].indicator.level = "";
return;
}
this.tab.tabs[index].indicator.message = msg.text;
this.tab.tabs[index].indicator.level = "warning";
},
tabInfo(index, msg) {
if (msg.length > 0) {
this.tab.tabs[index].indicator.message = msg;
this.tab.tabs[index].indicator.level = "info";
} else {
if (msg.toDismiss) {
if (
this.tab.tabs[index].indicator.message !== msg.text ||
this.tab.tabs[index].indicator.level !== "info"
) {
return;
}
this.tab.tabs[index].indicator.message = "";
this.tab.tabs[index].indicator.level = "";
return;
}
this.tab.tabs[index].indicator.message = msg.text;
this.tab.tabs[index].indicator.level = "info";
},
tabUpdated(index) {
this.$emit("tab-updated", this.tab.tabs);

View File

@@ -212,6 +212,15 @@
</button>
</div>
</fieldset>
<div
v-if="preloaderIDName.length > 0"
style="width: 1px; height: 1px; margin: 10px; position: absolute; top: 0; bottom: 0; overflow: hidden;"
>
<div :id="preloaderIDName">
{{ current.title || connector.name }} wizard
</div>
</div>
</form>
</template>
@@ -219,6 +228,8 @@
import "./connector.css";
import * as command from "../commands/commands.js";
const preloaderIDPrefix = "connector-resource-preload-control-";
function buildField(i, field) {
return {
verified: false,
@@ -267,6 +278,7 @@ export default {
currentConnector: null,
currentConnectorCloseWait: null,
current: buildEmptyCurrent(),
preloaderIDName: "",
working: false,
disabled: false,
cancelled: false
@@ -399,6 +411,13 @@ export default {
throw new Error("Cannot run wizard multiple times");
}
this.preloaderIDName =
preloaderIDPrefix +
this.getConnector()
.wizard.control()
.ui()
.toLowerCase();
this.currentConnectorCloseWait = (async () => {
while (!this.disabled) {
let next = this.buildCurrent(await this.getConnector().wizard.next());

View File

@@ -21,14 +21,18 @@
@import "~hack-font/build/web/hack.css";
#home-content > #home-content-preload-drop::before {
#connector-resource-preload-control-console {
font-family: Hack;
}
#connector-resource-preload-control-console::after {
content: " ";
font-family: Hack, monospace;
font-family: Hack;
font-weight: bold;
}
#home-content > #home-content-preload-drop::after {
#connector-resource-preload-control-console::before {
content: " ";
font-family: Hack, monospace;
font-family: Hack;
font-style: italic;
}
#home-content > .screen > .screen-screen > .screen-console {
@@ -144,5 +148,32 @@
}
#home-content > .screen > .screen-screen > .screen-console > .console-console {
font-family: Hack, monospace;
display: flex;
flex-direction: column;
justify-content: center;
justify-items: center;
color: #fff;
}
#home-content
> .screen
> .screen-screen
> .screen-console
> .console-console
> .console-loading {
text-align: center;
font-size: 1em;
font-weight: lighter;
}
#home-content
> .screen
> .screen-screen
> .screen-console
> .console-console
> .console-loading
> .console-loading-icon {
background: url(./busy.svg) 50% no-repeat;
width: 100%;
height: 100px;
}

View File

@@ -22,8 +22,16 @@
<div
class="console-console"
style="top: 0; right: 0; left: 0; bottom: 0; padding 0; margin: 0; z-index: 0; position: absolute; overflow: hidden"
:style="'font-family: ' + typeface"
>
<h2 style="display:none;">Console</h2>
<div class="console-loading">
<div class="console-loading-icon"></div>
<div class="console-loading-message">
Initializing console ...
</div>
</div>
</div>
<!--
@@ -91,7 +99,13 @@ import "xterm/css/xterm.css";
const termTypeFace = "Hack";
const termFallbackTypeFace = "monospace";
const termTypeFaceLoadTimeout = 10000;
const termTypeFaceLoadTimeout = 3000;
const termTypeFaceLoadError =
'Remote font "' +
termTypeFace +
'" is unavailable, using "' +
termFallbackTypeFace +
'" instead until the remote font is loaded';
const termDefaultFontSize = 16;
const termMinFontSize = 14;
const termMaxFontSize = 36;
@@ -205,7 +219,11 @@ class Term {
});
}
open(root, callbacks) {
setFont(value) {
this.term.setOption("fontFamily", value);
}
init(root, callbacks) {
this.term.open(root);
this.term.textarea.addEventListener("focus", callbacks.focus);
@@ -248,32 +266,6 @@ class Term {
this.refit();
}
init(root, callbacks) {
const self = this;
return Promise.all([
new FontFaceObserver(termTypeFace).load(null, termTypeFaceLoadTimeout),
new FontFaceObserver(termTypeFace, { weight: "bold" }).load(
null,
termTypeFaceLoadTimeout
)
])
.then(() => {
self.open(root, callbacks);
})
.catch(() => {
callbacks.warn(
"Unable to load remote font, using " +
termFallbackTypeFace +
" instead"
);
self.term.setOption("fontFamily", termFallbackTypeFace);
self.open(root, callbacks);
});
}
dispatch(event) {
try {
this.term.textarea.dispatchEvent(event);
@@ -388,6 +380,8 @@ export default {
return {
screenKeys: consoleScreenKeys,
term: new Term(this.control),
typeface: termTypeFace,
running: true,
runner: null
};
},
@@ -407,19 +401,87 @@ export default {
}
},
async mounted() {
this.running = true;
await this.init();
},
beforeDestroy() {
this.deinit();
this.running = false;
},
methods: {
loadRemoteFont(typeface, timeout) {
return Promise.all([
new FontFaceObserver(typeface).load(null, timeout),
new FontFaceObserver(typeface, {
weight: "bold"
}).load(null, timeout)
]);
},
retryLoadRemoteFont(typeface, timeout, onSuccess) {
const self = this;
self
.loadRemoteFont(typeface, timeout)
.then(() => {
onSuccess();
})
.catch(() => {
if (!self.running) {
return;
}
self.retryLoadRemoteFont(typeface, timeout, onSuccess);
});
},
async openTerm(root, callbacks) {
const self = this;
return self
.loadRemoteFont(termTypeFace, termTypeFaceLoadTimeout)
.then(() => {
if (!self.running) {
return;
}
root.innerHTML = "";
self.term.init(root, callbacks);
})
.catch(() => {
if (!self.running) {
return;
}
root.innerHTML = "";
callbacks.warn(termTypeFaceLoadError, false);
self.term.setFont(termFallbackTypeFace);
self.term.init(root, callbacks);
self.retryLoadRemoteFont(
termTypeFace,
termTypeFaceLoadTimeout,
() => {
if (!self.running) {
return;
}
self.term.setFont(termTypeFace);
callbacks.warn(termTypeFaceLoadError, true);
}
);
});
},
triggerActive() {
this.active ? this.activate() : this.deactivate();
},
async init() {
let self = this;
await this.term.init(
await this.openTerm(
this.$el.getElementsByClassName("console-console")[0],
{
focus(e) {
@@ -430,15 +492,20 @@ export default {
document.removeEventListener("keyup", self.localKeypress);
document.removeEventListener("keydown", self.localKeypress);
},
warn(msg) {
self.$emit("warning", msg);
warn(msg, toDismiss) {
self.$emit("warning", {
text: msg,
toDismiss: toDismiss
});
},
info(msg) {
self.$emit("info", msg);
info(msg, toDismiss) {
self.$emit("info", {
text: msg,
toDismiss: toDismiss
});
}
}
);
this.triggerActive();
this.runRunner();
},

View File

@@ -40,7 +40,7 @@
<div class="screen-screen" style="position: relative">
<component
:is="getComponent(screenInfo.control.ui())"
:is="getComponent(screenInfo.ui)"
:active="screen === idx"
:control="screenInfo.control"
:change="screenInfo.indicator"