// FILIUS' documentation mode measured on its own elements (JDocuElement on a // GUIDocumentationPanel, as the sidebar's drag makes them): a new text and a // new rectangle (size, colour, font), their menus (font size, style, colour, // the colour lists), the font and colour chosen there, the smallest size a // drag on an edge leaves (a text is dragged while it has the focus, in its // enlarged frame), how an element is painted in the documentation mode and // in the others (fill, border, transparency), the order in which the panel // stacks texts and rectangles, and a project saved with them and loaded // again (SzenarioVerwaltung.speichern, laden). The reference for the // editor's documentation mode (packages/doc/test/documentation.test.ts). // // Usage: java -cp filius.jar:lib/*:classes DocuRun import filius.gui.netzwerksicht.GUIDocuItem; import filius.gui.netzwerksicht.GUIDocumentationPanel; import filius.gui.netzwerksicht.JDocuColorSelector; import filius.gui.netzwerksicht.JDocuElement; import filius.rahmenprogramm.SzenarioVerwaltung; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Font; import java.awt.Window; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.FileOutputStream; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.MenuElement; import javax.swing.SwingUtilities; public class DocuRun { static final Map output = new LinkedHashMap<>(); static GUIDocumentationPanel panel; static void onEdt(Runnable action) throws Exception { SwingUtilities.invokeAndWait(action); Thread.sleep(200); } static List color(Color color) { List rgba = new ArrayList<>(); rgba.add(color.getRed()); rgba.add(color.getGreen()); rgba.add(color.getBlue()); rgba.add(color.getAlpha()); return rgba; } static Map font(Font font) { Map shown = new LinkedHashMap<>(); if (font == null) return null; shown.put("name", font.getName()); shown.put("style", font.getStyle()); shown.put("size", font.getSize()); return shown; } static Map element(JDocuElement element) { Map shown = new LinkedHashMap<>(); shown.put("bounds", List.of(element.getX(), element.getY(), element.getWidth(), element.getHeight())); shown.put("color", color(element.getColor())); shown.put("font", font(element.getFont())); shown.put("text", element.getText()); return shown; } static Map item(GUIDocuItem item) { Map shown = new LinkedHashMap<>(); shown.put("type", item.getType()); shown.put("text", item.getText()); shown.put("bounds", List.of(item.getX(), item.getY(), item.getWidth(), item.getHeight())); shown.put("color", color(item.getColor())); shown.put("font", font(item.getFont())); return shown; } static JPopupMenu shownMenu() { for (Window window : Window.getWindows()) { JPopupMenu found = find(window, JPopupMenu.class); if (found != null) return found; } return null; } static T find(Component component, Class type) { if (type.isInstance(component) && component.isShowing()) return type.cast(component); if (component instanceof Container) { for (Component child : ((Container) component).getComponents()) { T found = find(child, type); if (found != null) return found; } } return null; } /** The element's menu (showPopupMenu, private): each entry with its sub-entries or colours. */ static JPopupMenu openMenu(JDocuElement element) throws Exception { Method show = JDocuElement.class.getDeclaredMethod("showPopupMenu", int.class, int.class); show.setAccessible(true); onEdt(() -> { try { show.invoke(element, 5, 5); } catch (Exception error) { throw new RuntimeException(error); } }); return shownMenu(); } static List menu(JPopupMenu menu) { List entries = new ArrayList<>(); for (Component component : menu.getComponents()) { Map entry = new LinkedHashMap<>(); if (component instanceof JDocuColorSelector) { JDocuColorSelector selector = (JDocuColorSelector) component; entry.put("colors", selector.getText()); List colors = new ArrayList<>(); List chosen = new ArrayList<>(); JPanel samples = (JPanel) selector.getPopupMenu().getComponent(0); for (Component sample : samples.getComponents()) { colors.add(color(sample.getBackground())); chosen.add(((javax.swing.JComponent) sample).getBorder().getBorderInsets(sample).top == 3); } entry.put("list", colors); entry.put("chosen", chosen); entry.put("rows", ((java.awt.GridLayout) samples.getLayout()).getRows()); } else if (component instanceof JMenu) { JMenu sub = (JMenu) component; entry.put("menu", sub.getText()); List items = new ArrayList<>(); for (MenuElement each : sub.getSubElements()[0].getSubElements()) { JMenuItem item = (JMenuItem) each; items.add(item.getText() + (item.isSelected() ? " *" : "")); } entry.put("items", items); } else if (component instanceof JMenuItem) { entry.put("item", ((JMenuItem) component).getText()); } else { entry.put("separator", true); } entries.add(entry); } return entries; } static void choose(JPopupMenu menu, String text) throws Exception { for (Component component : menu.getComponents()) { if (component instanceof JMenu && !(component instanceof JDocuColorSelector)) { for (MenuElement each : ((JMenu) component).getSubElements()[0].getSubElements()) { JMenuItem item = (JMenuItem) each; if (item.getText().equals(text)) { onEdt(item::doClick); return; } } } } throw new IllegalStateException("no entry " + text); } static void closeMenu(JPopupMenu menu) throws Exception { onEdt(() -> menu.setVisible(false)); } static void pickColor(JPopupMenu menu, Color color) throws Exception { for (Component component : menu.getComponents()) { if (component instanceof JDocuColorSelector) { onEdt(() -> ((JDocuColorSelector) component).triggerMenuResponse(color)); return; } } } static MouseEvent mouse(Component on, int id, int x, int y) { return new MouseEvent(on, id, System.currentTimeMillis(), id == MouseEvent.MOUSE_DRAGGED ? MouseEvent.BUTTON1_DOWN_MASK : 0, x, y, 1, false, MouseEvent.BUTTON1); } /** A drag from a point of the element to another, in its coordinates (the edge decides what changes). */ static void drag(JDocuElement element, int fromX, int fromY, int toX, int toY) throws Exception { onEdt(() -> { element.dispatchEvent(mouse(element, MouseEvent.MOUSE_MOVED, fromX, fromY)); element.dispatchEvent(mouse(element, MouseEvent.MOUSE_PRESSED, fromX, fromY)); element.dispatchEvent(mouse(element, MouseEvent.MOUSE_DRAGGED, toX, toY)); element.dispatchEvent(mouse(element, MouseEvent.MOUSE_RELEASED, toX, toY)); }); } /** The element painted alone: the pixels at the given points, as RGBA. */ static Map pixels(JDocuElement element, int[][] points) throws Exception { Map shown = new LinkedHashMap<>(); BufferedImage image = new BufferedImage(element.getWidth(), element.getHeight(), BufferedImage.TYPE_INT_ARGB); onEdt(() -> { java.awt.Graphics2D graphics = image.createGraphics(); element.paint(graphics); graphics.dispose(); }); for (int[] point : points) { int argb = image.getRGB(point[0], point[1]); shown.put(point[0] + "," + point[1], List.of((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >>> 24) & 255)); } return shown; } public static void main(String[] args) throws Exception { try { run(args); } catch (Throwable error) { error.printStackTrace(); System.exit(1); } System.exit(0); } static void run(String[] args) throws Exception { JFrame frame = new JFrame(); List made = new ArrayList<>(); SwingUtilities.invokeAndWait(() -> { panel = new GUIDocumentationPanel(800, 600); frame.getContentPane().setLayout(null); frame.getContentPane().add(panel); frame.setSize(820, 640); frame.setVisible(true); // As the sidebar's drop: made by drag, placed, added to the panel (which gives it the focus). JDocuElement text = new JDocuElement(true, true); text.setLocation(100, 100); JDocuElement rectangle = new JDocuElement(false, true); rectangle.setLocation(300, 100); made.add(text); made.add(rectangle); }); JDocuElement text = made.get(0); JDocuElement rectangle = made.get(1); Map fresh = new LinkedHashMap<>(); fresh.put("text", element(text)); fresh.put("rectangle", element(rectangle)); output.put("new", fresh); onEdt(() -> panel.add(text)); Thread.sleep(300); Map focused = new LinkedHashMap<>(); focused.put("text", element(text)); onEdt(panel::removeElementsFocus); focused.put("textUnfocused", element(text)); output.put("dropped", focused); // The menus. JPopupMenu menu = openMenu(text); Map menus = new LinkedHashMap<>(); menus.put("text", menu(menu)); choose(menu, "Groß"); closeMenu(menu); List fonts = new ArrayList<>(); fonts.add(font(text.getFont())); menu = openMenu(text); menus.put("textAfterLarge", menu(menu)); choose(menu, "Kursiv"); closeMenu(menu); fonts.add(font(text.getFont())); menu = openMenu(text); choose(menu, "Fett"); closeMenu(menu); fonts.add(font(text.getFont())); menu = openMenu(text); choose(menu, "Normal"); // the first "Normal", the size menu's closeMenu(menu); fonts.add(font(text.getFont())); menu = openMenu(text); choose(menu, "Sehr Gro\u00df"); closeMenu(menu); fonts.add(font(text.getFont())); menu = openMenu(text); choose(menu, "Normal"); closeMenu(menu); menu = openMenu(text); pickColor(menu, new Color(0.31f, 0.51f, 0.74f)); closeMenu(menu); output.put("fonts", fonts); output.put("textColor", color(text.getColor())); onEdt(() -> panel.add(rectangle)); menu = openMenu(rectangle); menus.put("rectangle", menu(menu)); pickColor(menu, new Color(1f, 0.79f, 0.79f)); closeMenu(menu); output.put("rectangleColor", color(rectangle.getColor())); output.put("menus", menus); // The smallest sizes a drag leaves: a rectangle by its corner; a text, focused, by its corner, then unfocused. Map smallest = new LinkedHashMap<>(); onEdt(() -> rectangle.setLocalFocus(true)); drag(rectangle, rectangle.getWidth() - 2, rectangle.getHeight() - 2, 3, 3); smallest.put("rectangle", element(rectangle)); drag(rectangle, 1, 1, 200, 200); smallest.put("rectangleByItsTopLeft", element(rectangle)); onEdt(() -> text.setLocalFocus(true)); smallest.put("textFocused", element(text)); drag(text, text.getWidth() - 2, text.getHeight() - 2, 3, 3); smallest.put("textFocusedSmallest", element(text)); onEdt(panel::removeElementsFocus); smallest.put("textSmallest", element(text)); // A drag on an unfocused text does nothing (Java moves and sizes a text only in its enlarged frame). drag(text, 10, 10, 60, 60); smallest.put("textDraggedUnfocused", element(text)); output.put("smallest", smallest); // Painted: a rectangle and a text, in the documentation mode (enabled) unfocused and focused, and in the others. Map painted = new LinkedHashMap<>(); JDocuElement plain = new JDocuElement(false); JDocuElement words = new JDocuElement(true); onEdt(() -> { plain.setBounds(400, 300, 100, 50); plain.setColor(new Color(1f, 0.79f, 0.79f)); words.setBounds(400, 400, 100, 50); words.setText("Text"); panel.add(plain); panel.add(words); }); int[][] points = { { 50, 25 }, { 1, 25 }, { 2, 25 }, { 0, 25 } }; painted.put("rectangleEnabled", pixels(plain, points)); onEdt(() -> plain.setLocalFocus(true)); painted.put("rectangleFocused", pixels(plain, new int[][] { { 50, 25 }, { 3, 25 }, { 1, 25 } })); onEdt(() -> plain.setEnabled(false)); painted.put("rectangleDisabled", pixels(plain, points)); painted.put("textEnabled", pixels(words, new int[][] { { 0, 25 }, { 1, 25 }, { 50, 0 }, { 99, 25 } })); onEdt(() -> words.setEnabled(false)); painted.put("textDisabled", pixels(words, new int[][] { { 0, 25 }, { 1, 25 }, { 50, 0 } })); output.put("painted", painted); // The stacking: texts first, then rectangles, each in the list's order; the first component is on top. List items = new ArrayList<>(); JDocuElement[] elements = { new JDocuElement(true), new JDocuElement(false), new JDocuElement(true), new JDocuElement(false) }; String[] names = { "text 1", "rectangle 1", "text 2", "rectangle 2" }; onEdt(() -> { for (int index = 0; index < elements.length; index++) { elements[index].setBounds(50 + 10 * index, 50 + 10 * index, 100, 50); elements[index].setText(names[index]); elements[index].setName(names[index]); items.add(GUIDocuItem.createDocuItem(elements[index])); } panel.updateViewport(items, true); }); List stacking = new ArrayList<>(); for (Component component : panel.getComponents()) stacking.add(component.getName()); output.put("stacking", stacking); output.put("topAt", panel.getComponentAt(85, 85).getName()); // A project with documentation, saved and loaded again. List saved = new ArrayList<>(); JDocuElement italic = new JDocuElement(true); JDocuElement bold = new JDocuElement(true); JDocuElement pink = new JDocuElement(false); JDocuElement grey = new JDocuElement(false); onEdt(() -> { italic.setBounds(40, 40, 160, 60); italic.setText("Kursiv und mittel\nzweite Zeile"); italic.setFont(italic.getFont().deriveFont(Font.ITALIC, 18f)); italic.setColor(new Color(0.31f, 0.51f, 0.74f)); bold.setBounds(40, 140, 120, 50); bold.setText("Fett"); bold.setFont(bold.getFont().deriveFont(Font.BOLD, 24f)); pink.setBounds(20, 20, 300, 200); pink.setColor(new Color(1f, 0.79f, 0.79f)); grey.setBounds(400, 20, 100, 50); for (JDocuElement each : new JDocuElement[] { italic, bold, pink, grey }) saved.add(GUIDocuItem.createDocuItem(each)); }); List savedItems = new ArrayList<>(); for (GUIDocuItem each : saved) savedItems.add(item(each)); output.put("saved", savedItems); if (!SzenarioVerwaltung.getInstance().speichern(args[1], new ArrayList<>(), new ArrayList<>(), saved)) throw new IllegalStateException("not saved"); List loaded = new ArrayList<>(); SzenarioVerwaltung.getInstance().laden(args[1], new ArrayList<>(), new ArrayList<>(), loaded); List loadedItems = new ArrayList<>(); for (GUIDocuItem each : loaded) loadedItems.add(item(each)); output.put("loaded", loadedItems); try (FileOutputStream out = new FileOutputStream(args[0])) { out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8)); } System.out.println("documentation measured"); } }