This commit is contained in:
j3d1 2026-08-17 18:58:42 +02:00
parent 7d7730354e
commit accbaf3603

View file

@ -117,7 +117,7 @@
Disconnect Disconnect
</button> </button>
<button v-else-if="d.supported" class="btn btn-sm btn-outline-primary" <button v-else-if="d.supported" class="btn btn-sm btn-outline-primary"
:disabled="busy || connected !== null" @click="connect(d.index)"> :disabled="busy" @click="connect(d.index)">
Connect Connect
</button> </button>
</li> </li>
@ -267,6 +267,8 @@ export default {
devices: [], devices: [],
connected: null, connected: null,
tape: null,
labelBitmap: null,
value: this.prefill || "", value: this.prefill || "",
copies: 1, copies: 1,
@ -305,6 +307,16 @@ export default {
this.redrawFallback(); this.redrawFallback();
} }
}, },
// Covers connecting/disconnecting/switching printers - anything that changes the
// tape dimensions redraw() sizes the canvas from. flush: 'post' because the canvas
// itself only exists once `tape` is truthy (see the v-if/v-else in the template), so
// this has to run after Vue has actually mounted it, not before.
tape: {
handler() {
this.redraw();
},
flush: 'post'
},
}, },
methods: { methods: {
async guard(fn) { async guard(fn) {
@ -344,25 +356,32 @@ export default {
}); });
}, },
async closeConnection() {
if (this.connected) {
await this.blob.close();
this.connected = null;
this.tape = null;
}
},
connect(index) { connect(index) {
this.guard(async () => { this.guard(async () => {
// Only one open device connection at a time - pressing Connect on a different
// printer while one is already open implicitly disconnects it first, rather
// than requiring an explicit Disconnect click.
await this.closeConnection();
const device = this.devices[index]; const device = this.devices[index];
this.blob.setDevices([device]); this.blob.setDevices([device]);
await this.blob.open(device.vendorId, device.productId); await this.blob.open(device.vendorId, device.productId);
this.connected = device; this.connected = device;
const status = await this.blob.status(); const status = await this.blob.status();
// Setting `tape` alone is enough to redraw - see the tape watcher above.
this.tape = this.blob.can("print") ? tapeFromStatus(status) : null; this.tape = this.blob.can("print") ? tapeFromStatus(status) : null;
await nextTick();
this.redraw();
}); });
}, },
disconnect() { disconnect() {
this.guard(async () => { this.guard(() => this.closeConnection());
await this.blob.close();
this.connected = null;
this.tape = null;
});
}, },
redraw() { redraw() {
@ -374,6 +393,7 @@ export default {
if (!canvas) { if (!canvas) {
return; return;
} }
this.resizeObserver.observe(canvas.parentElement);
try { try {
const qr = QRCode.create(this.value); const qr = QRCode.create(this.value);
drawQrLabel(canvas, qr, this.tape); drawQrLabel(canvas, qr, this.tape);
@ -397,6 +417,7 @@ export default {
if (!canvas) { if (!canvas) {
return; return;
} }
this.resizeObserver.observe(canvas.parentElement);
try { try {
const qr = QRCode.create(this.value); const qr = QRCode.create(this.value);
drawQrSquare(canvas, qr); drawQrSquare(canvas, qr);
@ -441,13 +462,17 @@ export default {
}); });
}, },
handleResize() { /* Re-fits whichever canvas sits in a resized container - covers a window resize, but
also a sidebar toggle, a font finishing loading, or any other layout change that
isn't a window resize at all. Debounced since ResizeObserver can fire in bursts. */
handleContainerResize(entries) {
clearTimeout(this.resizeTimer); clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(() => { this.resizeTimer = setTimeout(() => {
if (this.usbSupported) { for (const entry of entries) {
this.redraw(); const canvas = entry.target.querySelector("canvas");
} else { if (canvas) {
this.redrawFallback(); this.fitZoom(canvas);
}
} }
}, 100); }, 100);
}, },
@ -458,12 +483,11 @@ export default {
}, },
created() { created() {
this.blob = null; this.blob = null;
this.tape = null; this.resizeObserver = null;
this.labelBitmap = null;
this.resizeTimer = null; this.resizeTimer = null;
}, },
async mounted() { async mounted() {
window.addEventListener("resize", this.handleResize); this.resizeObserver = new ResizeObserver(this.handleContainerResize);
if (!("usb" in navigator)) { if (!("usb" in navigator)) {
this.usbSupported = false; this.usbSupported = false;
await nextTick(); await nextTick();
@ -486,7 +510,7 @@ export default {
navigator.usb.removeEventListener("connect", this.onUsbChange); navigator.usb.removeEventListener("connect", this.onUsbChange);
navigator.usb.removeEventListener("disconnect", this.onUsbChange); navigator.usb.removeEventListener("disconnect", this.onUsbChange);
} }
window.removeEventListener("resize", this.handleResize); this.resizeObserver.disconnect();
clearTimeout(this.resizeTimer); clearTimeout(this.resizeTimer);
}, },
} }