fix: device id change UI event chain fixed; changing the Active IC's ID no longer breaks the UI

This commit is contained in:
Rachel Powers
2024-04-28 10:56:45 -07:00
parent f580cb2c53
commit 4ac823a1bc
6 changed files with 139 additions and 88 deletions

View File

@@ -285,11 +285,15 @@ impl VM {
device.borrow_mut().id = new_id;
self.devices.insert(new_id, device);
self.ics.iter().for_each(|(_id, ic)| {
ic.borrow().pins.borrow_mut().iter_mut().for_each(|pin| {
let mut ic_ref = ic.borrow_mut();
if ic_ref.device == old_id {
ic_ref.device = new_id;
}
ic_ref.pins.borrow_mut().iter_mut().for_each(|pin| {
if pin.is_some_and(|d| d == old_id) {
pin.replace(new_id);
}
})
});
});
self.networks.iter().for_each(|(_net_id, net)| {
if let Ok(mut net_ref) = net.try_borrow_mut() {

View File

@@ -35,9 +35,9 @@ export class IC10Editor extends BaseElement {
};
sessions: Map<number, Ace.EditSession>;
@state() active_session: number = 1;
@state() activeSession: number = 1;
active_line_markers: Map<number, number | null> = new Map();
activeLineMarkers: Map<number, number | null> = new Map();
languageProvider?: LanguageProvider;
// ui: IC10EditorUI;
@@ -78,56 +78,35 @@ export class IC10Editor extends BaseElement {
};
this.sessions = new Map();
this.active_line_markers = new Map();
this.activeLineMarkers = new Map();
// this.ui = new IC10EditorUI(this);
}
protected render() {
const result = html`
<div
id="editorContainer"
style="height: 100%; width: 100%; position: relative; z-index: auto;"
>
<div
id="editor"
style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0; isolation: isolate;"
></div>
<div id="editorContainer" style="height: 100%; width: 100%; position: relative; z-index: auto;">
<div id="editor" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0; isolation: isolate;">
</div>
<div id="editorStatusbar"></div>
</div>
<sl-dialog label="Editor Settings" class="dialog-focus e-settings-dialog">
<sl-radio-group
id="editorKeyboardRadio"
label="Editor Keyboard Bindings"
value=${this.settings.keyboard}
>
<sl-radio-group id="editorKeyboardRadio" label="Editor Keyboard Bindings" value=${this.settings.keyboard}>
<sl-radio-button value="ace">Ace</sl-radio-button>
<sl-radio-button value="vim">Vim</sl-radio-button>
<sl-radio-button value="emacs">Emacs</sl-radio-button>
<sl-radio-button value="sublime">Sublime</sl-radio-button>
<sl-radio-button value="vscode">VS Code</sl-radio-button>
</sl-radio-group>
<sl-radio-group
id="editorCursorRadio"
label="Editor Cursor Style"
value=${this.settings.cursor}
>
<sl-radio-group id="editorCursorRadio" label="Editor Cursor Style" value=${this.settings.cursor}>
<sl-radio-button value="ace">Ace</sl-radio-button>
<sl-radio-button value="slim">Slim</sl-radio-button>
<sl-radio-button value="smooth">Smooth</sl-radio-button>
<sl-radio-button value="smooth slim">Smooth And Slim</sl-radio-button>
<sl-radio-button value="wide">Wide</sl-radio-button>
</sl-radio-group>
<sl-input
id="editorFontSize"
label="Font Size"
type="number"
value="${this.settings.fontSize}"
></sl-input>
<sl-switch
id="editorRelativeLineNumbers"
?checked=${this.settings.relativeLineNumbers}
>
<sl-input id="editorFontSize" label="Font Size" type="number" value="${this.settings.fontSize}"></sl-input>
<sl-switch id="editorRelativeLineNumbers" ?checked=${this.settings.relativeLineNumbers}>
Relative Line Numbers
</sl-switch>
</sl-dialog>
@@ -230,12 +209,9 @@ export class IC10Editor extends BaseElement {
// characterData: false,
// });
this.sessions.set(this.active_session, this.editor.getSession());
this.bindSession(
this.active_session,
this.sessions.get(this.active_session),
);
this.active_line_markers.set(this.active_session, null);
this.sessions.set(this.activeSession, this.editor.getSession());
this.bindSession(this.activeSession, this.sessions.get(this.activeSession));
this.activeLineMarkers.set(this.activeSession, null);
const worker = await setupLspWorker();
this.setupLsp(worker);
@@ -264,35 +240,35 @@ export class IC10Editor extends BaseElement {
const that = this;
const app = await window.App.get();
app.session.onLoad(((e: CustomEvent) => {
app.session.onLoad((_e) => {
const session = app.session;
const updated_ids: number[] = [];
for (const [id, code] of session.programs) {
updated_ids.push(id);
that.createOrSetSession(id, code);
}
that.activateSession(that.active_session);
that.activateSession(that.activeSession);
for (const [id, _] of that.sessions) {
if (!updated_ids.includes(id)) {
that.destroySession(id);
}
}
}) as EventListener);
});
app.session.loadFromFragment();
app.session.onActiveLine(((e: CustomEvent) => {
app.session.onActiveLine((e) => {
const session = app.session;
const id: number = e.detail;
const active_line = session.getActiveLine(id);
if (typeof active_line !== "undefined") {
const marker = that.active_line_markers.get(id);
const marker = that.activeLineMarkers.get(id);
if (marker) {
that.sessions.get(id)?.removeMarker(marker);
that.active_line_markers.set(id, null);
that.activeLineMarkers.set(id, null);
}
const session = that.sessions.get(id);
if (session) {
that.active_line_markers.set(
that.activeLineMarkers.set(
id,
session.addMarker(
new Range(active_line, 0, active_line, 1),
@@ -301,14 +277,30 @@ export class IC10Editor extends BaseElement {
true,
),
);
if (that.active_session == id) {
if (that.activeSession == id) {
// editor.resize(true);
// TODO: Scroll to line if vm was stepped
//that.editor.scrollToLine(active_line, true, true, ()=>{})
}
}
}
}) as EventListener);
});
app.session.onIDChange((e) => {
const oldID = e.detail.old;
const newID = e.detail.new;
if (this.sessions.has(oldID)) {
this.sessions.set(newID, this.sessions.get(oldID));
this.sessions.delete(oldID);
}
if (this.activeLineMarkers.has(oldID)) {
this.activeLineMarkers.set(newID, this.activeLineMarkers.get(oldID));
this.activeLineMarkers.delete(oldID);
}
if (this.activeSession === oldID) {
this.activeSession = newID;
}
});
// change -> possibility to allow saving the value without having to wait for blur
editor.on("change", () => this.editorChangeAction());
@@ -521,7 +513,7 @@ export class IC10Editor extends BaseElement {
const mode = ace.require(this.mode);
const options = mode?.options ?? {};
this.languageProvider?.setSessionOptions(session, options);
this.active_session = session_id;
this.activeSession = session_id;
return true;
}
@@ -569,7 +561,7 @@ export class IC10Editor extends BaseElement {
}
const session = this.sessions.get(session_id);
this.sessions.delete(session_id);
if ((this.active_session = session_id)) {
if ((this.activeSession = session_id)) {
this.activateSession(this.sessions.entries().next().value);
}
session?.destroy();

View File

@@ -1,4 +1,3 @@
import type { ICError, FrozenVM, SlotType } from "ic10emu_wasm";
import { App } from "./app";
@@ -57,7 +56,25 @@ export class Session extends EventTarget {
);
}
onActiveIc(callback: EventListenerOrEventListenerObject) {
changeID(oldID: number, newID: number) {
if (this.programs.has(oldID)) {
this.programs.set(newID, this.programs.get(oldID));
this.programs.delete(oldID);
}
this.dispatchEvent(
new CustomEvent("session-id-change", {
detail: { old: oldID, new: newID },
}),
);
}
onIDChange(
callback: (e: CustomEvent<{ old: number; new: number }>) => any,
) {
this.addEventListener("session-id-change", callback);
}
onActiveIc(callback: (e: CustomEvent<number>) => any,) {
this.addEventListener("session-active-ic", callback);
}
@@ -98,11 +115,11 @@ export class Session extends EventTarget {
);
}
onErrors(callback: EventListenerOrEventListenerObject) {
onErrors(callback: (e: CustomEvent<number[]>) => any) {
this.addEventListener("session-errors", callback);
}
onLoad(callback: EventListenerOrEventListenerObject) {
onLoad(callback: (e: CustomEvent<Session>) => any) {
this.addEventListener("session-load", callback);
}
@@ -114,7 +131,7 @@ export class Session extends EventTarget {
);
}
onActiveLine(callback: EventListenerOrEventListenerObject) {
onActiveLine(callback: (e: CustomEvent<number>) => any) {
this.addEventListener("active-line", callback);
}

View File

@@ -111,60 +111,80 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
connectedCallback(): void {
const root = super.connectedCallback();
window.VM.get().then((vm) =>
window.VM.get().then((vm) => {
vm.addEventListener(
"vm-device-modified",
this._handleDeviceModified.bind(this),
),
);
window.VM.get().then((vm) =>
);
vm.addEventListener(
"vm-devices-update",
this._handleDevicesModified.bind(this),
),
);
);
vm.addEventListener(
"vm-device-id-change",
this._handleDeviceIdChange.bind(this),
);
});
this.updateDevice();
return root;
}
disconnectedCallback(): void {
window.VM.get().then((vm) =>
window.VM.get().then((vm) => {
vm.removeEventListener(
"vm-device-modified",
this._handleDeviceModified.bind(this),
),
);
window.VM.get().then((vm) =>
);
vm.removeEventListener(
"vm-devices-update",
this._handleDevicesModified.bind(this),
),
);
);
vm.removeEventListener(
"vm-device-id-change",
this._handleDeviceIdChange.bind(this),
);
});
}
_handleDeviceModified(e: CustomEvent) {
const id = e.detail;
const activeIc = window.VM.vm.activeIC;
const activeIcId = window.App.app.session.activeIC;
if (this.deviceID === id) {
this.updateDevice();
} else if (id === activeIc.id && this.deviceSubscriptions.includes("active-ic")) {
} else if (
id === activeIcId &&
this.deviceSubscriptions.includes("active-ic")
) {
this.updateDevice();
}
}
_handleDevicesModified(e: CustomEvent<number[]>) {
const activeIc = window.VM.vm.activeIC;
const activeIcId = window.App.app.session.activeIC;
const ids = e.detail;
if (ids.includes(this.deviceID)) {
this.updateDevice()
} else if (ids.includes(activeIc.id) && this.deviceSubscriptions.includes("active-ic")) {
this.updateDevice();
} else if (
ids.includes(activeIcId) &&
this.deviceSubscriptions.includes("active-ic")
) {
this.updateDevice();
}
}
_handleDeviceIdChange(e: CustomEvent<{ old: number; new: number }>) {
if (this.deviceID === e.detail.old) {
this.deviceID = e.detail.new;
}
}
updateDevice() {
this.device = window.VM.vm.devices.get(this.deviceID)!;
if (typeof this.device === "undefined") {
return;
}
for (const sub of this.deviceSubscriptions) {
if (typeof sub === "string") {
if (sub == "name") {
@@ -220,16 +240,21 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
}
}
} else {
if ( "field" in sub ) {
if ("field" in sub) {
const fields = this.device.fields;
if (this.fields.get(sub.field) !== fields.get(sub.field)) {
this.fields = fields;
}
} else if ( "slot" in sub) {
} else if ("slot" in sub) {
const slots = this.device.slots;
if (typeof this.slots === "undefined" || this.slots.length < sub.slot) {
if (
typeof this.slots === "undefined" ||
this.slots.length < sub.slot
) {
this.slots = slots;
} else if (!structuralEqual(this.slots[sub.slot], slots[sub.slot])) {
} else if (
!structuralEqual(this.slots[sub.slot], slots[sub.slot])
) {
this.slots = slots;
}
}

View File

@@ -361,7 +361,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
const val = parseIntWithHexOrBinary(input.value);
if (!isNaN(val)) {
window.VM.get().then((vm) => {
if (!vm.changeDeviceId(this.deviceID, val)) {
if (!vm.changeDeviceID(this.deviceID, val)) {
input.value = this.deviceID.toString();
}
});

View File

@@ -22,7 +22,7 @@ export interface ToastMessage {
}
export interface CacheDeviceRef extends DeviceRef {
dirty: boolean
dirty: boolean;
}
function cachedDeviceRef(ref: DeviceRef) {
@@ -43,12 +43,12 @@ function cachedDeviceRef(ref: DeviceRef) {
},
set(target, prop, value) {
if (prop === "dirty") {
slotsDirty = value
slotsDirty = value;
return true;
}
return Reflect.set(target, prop, value)
}
}) as CacheDeviceRef
return Reflect.set(target, prop, value);
},
}) as CacheDeviceRef;
}
class VirtualMachine extends EventTarget {
@@ -264,13 +264,22 @@ class VirtualMachine extends EventTarget {
this.dispatchEvent(new CustomEvent("vm-message", { detail: message }));
}
changeDeviceId(old_id: number, new_id: number): boolean {
changeDeviceID(oldID: number, newID: number): boolean {
try {
this.ic10vm.changeDeviceId(old_id, new_id);
this.updateDevices();
if (this.app.session.activeIC === old_id) {
this.app.session.activeIC = new_id;
this.ic10vm.changeDeviceId(oldID, newID);
if (this.app.session.activeIC === oldID) {
this.app.session.activeIC = newID;
}
this.updateDevices();
this.dispatchEvent(
new CustomEvent("vm-device-id-change", {
detail: {
old: oldID,
new: newID,
},
}),
);
this.app.session.changeID(oldID, newID);
return true;
} catch (err) {
this.handleVmError(err);
@@ -430,7 +439,11 @@ class VirtualMachine extends EventTarget {
}
}
setDeviceSlotOccupant(id: number, index: number, template: SlotOccupantTemplate): boolean {
setDeviceSlotOccupant(
id: number,
index: number,
template: SlotOccupantTemplate,
): boolean {
const device = this._devices.get(id);
if (typeof device !== "undefined") {
try {