// How the editor paints a documentation item, against the pixels Java's // JDocuElement paints (packages/doc/test/documentation.java.json, DocuRun): // a rectangle in the documentation mode and in the others, its border, the // text's border; the focused text's frame; where an edge is taken hold of — // and the editor's own dark paint, which Java has not. import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { darkRectangleBorder, darkRectangleFill, darkTextColor, edgesAt, fontFamily, holdFrame, rectangleBorder, rectangleFill, TEXT_BORDER, FOCUS_BORDER, FOCUS_BAND, textColor } from '../src/canvas/documentationPaint.js'; type Rgba = [number, number, number, number]; const java = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../doc/test/documentation.java.json'), 'utf8')) as { readonly painted: Record>; readonly dropped: { readonly text: { readonly bounds: number[] }; readonly textUnfocused: { readonly bounds: number[] } }; }; const pink = { red: 1, green: 201 / 255, blue: 201 / 255, alpha: 1 }; const css = ([red, green, blue, alpha]: Rgba) => (alpha === 255 ? `rgb(${red}, ${green}, ${blue})` : `rgba(${red}, ${green}, ${blue}, ${alpha / 255})`); describe("Java's paint of the documentation", () => { it('fills a rectangle at an opacity of 200 in the documentation mode, else opaque and lighter; its border four fifths of its colour', () => { const { rectangleEnabled, rectangleDisabled, rectangleFocused, textEnabled, textDisabled } = java.painted; expect(rectangleFill(pink, true)).toBe(css(rectangleEnabled!['50,25']!)); expect(rectangleFill(pink, false)).toBe(css(rectangleDisabled!['50,25']!)); expect(rectangleBorder(pink)).toBe(css(rectangleEnabled!['1,25']!)); expect(rectangleBorder(pink)).toBe(css(rectangleDisabled!['1,25']!)); // The focus: Java's blue border and translucent band (its alpha 77 of 255 is 0.3). expect(FOCUS_BORDER).toBe(css(rectangleFocused!['1,25']!)); const [red, green, blue, alpha] = rectangleFocused!['3,25']!; expect(FOCUS_BAND).toBe(`rgba(${red}, ${green}, ${blue}, ${Math.round((alpha / 255) * 10) / 10})`); // A text's border only in the documentation mode. expect(TEXT_BORDER).toBe(css(textEnabled!['0,25']!)); expect(textDisabled!['0,25']![3]).toBe(0); // Java's integer arithmetic: 255 + (c − 255) · 8 / 10 cut towards zero, c · 4 / 5 cut. expect(rectangleFill({ red: 0, green: 1, blue: 7 / 255, alpha: 1 }, false)).toBe('rgb(51, 255, 57)'); expect(rectangleBorder({ red: 0, green: 1, blue: 7 / 255, alpha: 1 })).toBe('rgb(0, 204, 5)'); // Where rounding would differ: (3 − 255) · 8 / 10 = −201.6 is −201 in Java; 3 · 4 / 5 = 2.4 is 2, 254 · 4 / 5 = 203.2 is 203. expect(rectangleFill({ red: 3 / 255, green: 254 / 255, blue: 1, alpha: 1 }, false)).toBe('rgb(54, 255, 255)'); expect(rectangleBorder({ red: 3 / 255, green: 254 / 255, blue: 4 / 255, alpha: 1 })).toBe('rgb(2, 203, 3)'); expect(textColor({ red: 0, green: 0, blue: 1, alpha: 0.5 })).toBe('rgba(0, 0, 255, 0.5)'); }); it('paints a rectangle in the dark scheme as a tint of its colour, stronger while documenting, and draws a text towards light', () => { const grey = { red: 0.8, green: 0.8, blue: 0.8, alpha: 1 }; expect(darkRectangleFill(grey, true)).toBe('rgba(204, 204, 204, 0.28)'); expect(darkRectangleFill(grey, false)).toBe('rgba(204, 204, 204, 0.2)'); expect(darkRectangleBorder({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe('rgba(255, 0, 0, 0.7)'); expect(darkTextColor({ red: 0, green: 0, blue: 0, alpha: 1 })).toBe('color-mix(in oklab, rgba(0, 0, 0, 1) 15%, var(--canvas-label))'); }); it("grows a focused text's frame as Java does, and finds the edges a drag takes", () => { const [x, y, width, height] = java.dropped.textUnfocused.bounds as [number, number, number, number]; const [fx, fy, fw, fh] = java.dropped.text.bounds as [number, number, number, number]; expect(holdFrame({ kind: 'text', x, y, width, height }, true)).toEqual({ x: fx, y: fy, width: fw, height: fh }); expect(holdFrame({ kind: 'text', x, y, width, height }, false)).toEqual({ kind: 'text', x, y, width, height }); expect(holdFrame({ kind: 'rectangle', x, y, width, height }, true)).toEqual({ kind: 'rectangle', x, y, width, height }); const frame = { x: 0, y: 0, width: 100, height: 50 }; expect(edgesAt(frame, { x: 50, y: 25 }, 7)).toEqual({ left: false, right: false, top: false, bottom: false }); expect(edgesAt(frame, { x: 7, y: 25 }, 7)).toEqual({ left: true, right: false, top: false, bottom: false }); expect(edgesAt(frame, { x: 93, y: 49 }, 7)).toEqual({ left: false, right: true, top: false, bottom: true }); expect(edgesAt(frame, { x: 92, y: 8 }, 7)).toEqual({ left: false, right: false, top: false, bottom: false }); expect(edgesAt(frame, { x: 3, y: 3 }, 7)).toEqual({ left: true, right: false, top: true, bottom: false }); // Narrower than two bands: both edges, of which the drag takes the left (resizedFrame). expect(edgesAt({ x: 0, y: 0, width: 10, height: 10 }, { x: 5, y: 5 }, 7)).toEqual({ left: true, right: true, top: true, bottom: true }); }); it("writes Java's logical fonts as the browser's", () => { expect(['Dialog', 'SansSerif', 'Serif', 'Monospaced', 'DialogInput', 'Comic "Neue"'].map(fontFamily)).toEqual([ 'sans-serif', 'sans-serif', 'serif', 'monospace', 'monospace', '"Comic Neue", sans-serif', ]); }); });