import * as loader from "tc-loader"; import {Stage} from "tc-loader"; import { ClientIcon, spriteEntries as kClientSpriteEntries, spriteHeight as kClientSpriteHeight, spriteUrl as kClientSpriteUrl, spriteWidth as kClientSpriteWidth, } from "svg-sprites/client-icons"; import {LogCategory, logDebug} from "tc-shared/log"; import {ChannelTreeDragData, ChannelTreeDragEntry} from "tc-shared/ui/tree/Definitions"; let spriteImage: HTMLImageElement; async function initializeIconSpreedSheet() { spriteImage = new Image(kClientSpriteWidth, kClientSpriteHeight); spriteImage.src = loader.config.baseUrl + kClientSpriteUrl; await new Promise((resolve, reject) => { spriteImage.onload = resolve; spriteImage.onerror = () => reject("failed to load client icon sprite"); }); } function paintClientIcon(context: CanvasRenderingContext2D, icon: ClientIcon, offsetX: number, offsetY: number, width: number, height: number) { const sprite = kClientSpriteEntries.find(e => e.className === icon); if(!sprite) return undefined; context.drawImage(spriteImage, sprite.xOffset, sprite.yOffset, sprite.width, sprite.height, offsetX, offsetY, width, height); } export type DragImageEntryType = { icon: ClientIcon, name: string } export function generateDragElement(entries: DragImageEntryType[]) : HTMLElement { const totalHeight = entries.length * 18 + 2; /* the two extra for "low" letters like "gyj" etc. */ const totalWidth = 250; let offsetY = 0; let offsetX = 20; const canvas = document.createElement("canvas"); document.body.appendChild(canvas); canvas.height = totalHeight; canvas.width = totalWidth; /* TODO: With font size? */ const ctx = canvas.getContext("2d"); { ctx.textAlign = "left"; ctx.textBaseline = "bottom"; ctx.font = "700 16px Roboto, Helvetica, Arial, sans-serif"; for(const entry of entries) { /* ctx.strokeStyle = "red"; ctx.moveTo(offsetX, offsetY); ctx.lineTo(offsetX + 1000, offsetY); ctx.stroke(); */ ctx.fillStyle = "black"; paintClientIcon(ctx, entry.icon, offsetX + 1, offsetY + 1, 16, 16); ctx.fillText(entry.name, offsetX + 20, offsetY + 19); offsetY += 18; /* ctx.strokeStyle = "blue"; ctx.moveTo(offsetX, offsetY); ctx.lineTo(offsetX + 1000, offsetY); ctx.stroke(); */ } } canvas.style.position = "absolute"; canvas.style.zIndex = "100000"; canvas.style.top = "0"; canvas.style.left = -canvas.width + "px"; setTimeout(() => { canvas.remove(); }, 50); return canvas as any; } const kDragDataType = "application/x-teaspeak-channel-move"; const kDragHandlerPrefix = "application/x-teaspeak-handler-"; const kDragTypePrefix = "application/x-teaspeak-type-"; export function setupDragData(transfer: DataTransfer, handlerId: string, entries: ChannelTreeDragEntry[], type: string) { let data: ChannelTreeDragData = { version: 1, handlerId: handlerId, entries: entries, type: type }; transfer.effectAllowed = "all" transfer.dropEffect = "move"; transfer.setData(kDragHandlerPrefix + handlerId, ""); transfer.setData(kDragTypePrefix + type, ""); transfer.setData(kDragDataType, JSON.stringify(data)); } export function parseDragData(transfer: DataTransfer) : ChannelTreeDragData | undefined { const rawData = transfer.getData(kDragDataType); if(!rawData) { return undefined; } try { const data = JSON.parse(rawData) as ChannelTreeDragData; if(data.version !== 1) { throw tra("invalid data version {}", data.version); } return data; } catch (error) { logDebug(LogCategory.GENERAL, tr("Drag data parsing failed: %o"), error); return undefined; } } export function getDragInfo(transfer: DataTransfer) : { handlerId: string, type: string } | undefined { const keys = [...transfer.items].filter(e => e.kind === "string").map(e => e.type); const handlerId = keys.find(e => e.startsWith(kDragHandlerPrefix))?.substr(kDragHandlerPrefix.length); const type = keys.find(e => e.startsWith(kDragTypePrefix))?.substr(kDragTypePrefix.length); if(!handlerId || !type) { return undefined; } return { handlerId: handlerId, type: type } } loader.register_task(Stage.JAVASCRIPT_INITIALIZING, { name: "Icon sprite loader", function: async () => await initializeIconSpreedSheet(), priority: 10 });