The surface turns dark with light labels and cables and a faint light outline round Java's icons; documentation rectangles become a tint of their colour and texts are drawn towards light; the mode buttons use Java's pictures freed from their grey ground (tools/dark-pictures/make.py); native controls follow color-scheme; the desktop's program windows paint through Java-look tokens whose light values are Java's own. The exported image and the report keep Java's colours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
79 lines
5.5 KiB
TypeScript
79 lines
5.5 KiB
TypeScript
// 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<string, Record<string, Rgba>>;
|
||
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',
|
||
]);
|
||
});
|
||
});
|