| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001 |
- package com.mes.ui;
- import com.alibaba.fastjson2.JSON;
- import com.alibaba.fastjson2.JSONArray;
- import com.alibaba.fastjson2.JSONObject;
- import javax.swing.BorderFactory;
- import javax.swing.BoxLayout;
- import javax.swing.JButton;
- import javax.swing.JComponent;
- import javax.swing.JLabel;
- import javax.swing.JOptionPane;
- import javax.swing.JPanel;
- import javax.swing.JSplitPane;
- import javax.swing.JTextField;
- import javax.swing.KeyStroke;
- import javax.swing.SwingConstants;
- import javax.swing.SwingUtilities;
- import javax.swing.TransferHandler;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.Component;
- import java.awt.Container;
- import java.awt.Cursor;
- import java.awt.Dimension;
- import java.awt.Font;
- import java.awt.Graphics;
- import java.awt.GridLayout;
- import java.awt.Point;
- import java.awt.Rectangle;
- import java.awt.datatransfer.DataFlavor;
- import java.awt.datatransfer.StringSelection;
- import java.awt.event.ActionEvent;
- import java.awt.event.FocusAdapter;
- import java.awt.event.FocusEvent;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.io.File;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.nio.file.Files;
- import java.util.ArrayDeque;
- import java.util.Deque;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
- public class VisualUiEditorPanel extends JPanel {
- private static final int CANVAS_WIDTH = 990;
- private static final int CANVAS_HEIGHT = 550;
- private static final int GRID_SIZE = 8;
- private static final int HANDLE_SIZE = 10;
- private static final String DESIGN_FILE = "ui-designs/op40-main.ui.json";
- private final File designFile;
- private final Container sourceContainer;
- private final Map<String, Component> sourceComponents = new HashMap<String, Component>();
- private final JPanel canvas;
- private JSplitPane sideContent;
- private JButton sideToggleButton;
- private final JLabel statusLabel;
- private final Deque<String> undoStack = new ArrayDeque<String>();
- private final Deque<String> redoStack = new ArrayDeque<String>();
- private JTextField textField;
- private JTextField xField;
- private JTextField yField;
- private JTextField widthField;
- private JTextField heightField;
- private JSONObject schema;
- private String selectedId;
- private boolean dirty;
- public VisualUiEditorPanel() {
- this(null);
- }
- public VisualUiEditorPanel(Container sourceContainer) {
- this.sourceContainer = sourceContainer;
- this.designFile = new File(System.getProperty("user.dir"), DESIGN_FILE);
- this.schema = loadSchema();
- if (sourceContainer != null) {
- indexSourceComponents(sourceContainer, sourceComponents);
- }
- setLayout(new BorderLayout(8, 8));
- setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
- JPanel palette = createPalette();
- JPanel inspector = createInspector();
- this.canvas = createCanvas();
- this.sideContent = new JSplitPane(JSplitPane.VERTICAL_SPLIT, palette, inspector);
- this.sideToggleButton = new JButton("<");
- this.statusLabel = new JLabel("Ready");
- sideContent.setResizeWeight(0.35);
- sideContent.setPreferredSize(new Dimension(250, 500));
- add(canvas, BorderLayout.CENTER);
- add(statusLabel, BorderLayout.SOUTH);
- installShortcuts();
- renderAll();
- }
- private JPanel createPalette() {
- JPanel panel = new JPanel(new GridLayout(0, 1, 6, 6));
- panel.setBorder(BorderFactory.createTitledBorder("Palette"));
- addPaletteButton(panel, "Label", "label");
- addPaletteButton(panel, "Button", "button");
- addPaletteButton(panel, "Text Field", "textfield");
- addPaletteButton(panel, "Panel", "panel");
- return panel;
- }
- private void addPaletteButton(JPanel panel, String title, final String type) {
- JButton button = new JButton(title);
- button.setFocusable(false);
- button.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(ActionEvent e) {
- addComponentAt(type, 40 + schema.getJSONArray("components").size() * 16, 40 + schema.getJSONArray("components").size() * 16);
- }
- });
- button.setTransferHandler(new TransferHandler("text") {
- protected java.awt.datatransfer.Transferable createTransferable(JComponent c) {
- return new StringSelection(type);
- }
- public int getSourceActions(JComponent c) {
- return COPY;
- }
- });
- button.addMouseMotionListener(new MouseAdapter() {
- public void mouseDragged(MouseEvent e) {
- JComponent component = (JComponent) e.getSource();
- component.getTransferHandler().exportAsDrag(component, e, TransferHandler.COPY);
- }
- });
- panel.add(button);
- }
- public JPanel createExternalToolPanel() {
- final JPanel panel = new JPanel(new BorderLayout());
- sideToggleButton.setFocusable(false);
- sideToggleButton.setPreferredSize(new Dimension(28, 40));
- sideToggleButton.setToolTipText("Collapse palette");
- sideToggleButton.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(ActionEvent e) {
- toggleSideBar(panel);
- }
- });
- panel.add(sideToggleButton, BorderLayout.WEST);
- panel.add(sideContent, BorderLayout.CENTER);
- return panel;
- }
- private void toggleSideBar(JPanel panel) {
- boolean expanded = sideContent.getParent() == panel;
- if (expanded) {
- panel.remove(sideContent);
- sideToggleButton.setText(">");
- sideToggleButton.setToolTipText("Expand palette");
- panel.setPreferredSize(new Dimension(30, 500));
- } else {
- panel.add(sideContent, BorderLayout.CENTER);
- sideToggleButton.setText("<");
- sideToggleButton.setToolTipText("Collapse palette");
- panel.setPreferredSize(null);
- }
- panel.revalidate();
- panel.repaint();
- java.awt.Window window = SwingUtilities.getWindowAncestor(panel);
- if (window != null) {
- window.pack();
- }
- }
- private JPanel createInspector() {
- JPanel panel = new JPanel();
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- panel.setBorder(BorderFactory.createTitledBorder("Properties"));
- textField = addProperty(panel, "Text");
- xField = addProperty(panel, "X");
- yField = addProperty(panel, "Y");
- widthField = addProperty(panel, "Width");
- heightField = addProperty(panel, "Height");
- addButton(panel, "Apply", new Runnable() {
- public void run() {
- applyInspector();
- }
- });
- addButton(panel, "Delete", new Runnable() {
- public void run() {
- deleteSelected();
- }
- });
- addButton(panel, "Import Current", new Runnable() {
- public void run() {
- importCurrentLayout();
- }
- });
- addButton(panel, "Apply To Live", new Runnable() {
- public void run() {
- applyToSourceContainer();
- }
- });
- addButton(panel, "Save", new Runnable() {
- public void run() {
- saveDesign();
- }
- });
- return panel;
- }
- private void addButton(JPanel panel, String title, final Runnable runnable) {
- JButton button = new JButton(title);
- button.setAlignmentX(Component.LEFT_ALIGNMENT);
- button.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(ActionEvent e) {
- runnable.run();
- }
- });
- panel.add(button);
- }
- private JTextField addProperty(JPanel panel, String label) {
- JLabel jLabel = new JLabel(label);
- jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
- JTextField field = new JTextField();
- field.setMaximumSize(new Dimension(Integer.MAX_VALUE, 28));
- field.setAlignmentX(Component.LEFT_ALIGNMENT);
- field.addFocusListener(new FocusAdapter() {
- public void focusLost(FocusEvent e) {
- applyInspector();
- }
- });
- panel.add(jLabel);
- panel.add(field);
- return field;
- }
- private JPanel createCanvas() {
- JPanel panel = new JPanel(null);
- panel.setBackground(new Color(245, 247, 250));
- panel.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
- panel.setBorder(BorderFactory.createLineBorder(new Color(180, 190, 200)));
- panel.setFocusable(true);
- panel.addMouseListener(new MouseAdapter() {
- public void mousePressed(MouseEvent e) {
- selectedId = null;
- updateInspector();
- renderAll();
- canvas.requestFocusInWindow();
- }
- });
- panel.setTransferHandler(new TransferHandler() {
- public boolean canImport(TransferSupport support) {
- return support.isDataFlavorSupported(DataFlavor.stringFlavor);
- }
- public boolean importData(TransferSupport support) {
- if (!canImport(support)) {
- return false;
- }
- try {
- String type = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
- Point point = support.getDropLocation().getDropPoint();
- addComponentAt(type, point.x, point.y);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
- });
- return panel;
- }
- private void installShortcuts() {
- bindShortcut("save", KeyStroke.getKeyStroke("control S"), new Runnable() {
- public void run() {
- saveDesign();
- }
- });
- bindShortcut("undo", KeyStroke.getKeyStroke("control Z"), new Runnable() {
- public void run() {
- undo();
- }
- });
- bindShortcut("redo", KeyStroke.getKeyStroke("control Y"), new Runnable() {
- public void run() {
- redo();
- }
- });
- bindShortcut("redoShift", KeyStroke.getKeyStroke("control shift Z"), new Runnable() {
- public void run() {
- redo();
- }
- });
- bindShortcut("delete", KeyStroke.getKeyStroke("DELETE"), new Runnable() {
- public void run() {
- deleteSelected();
- }
- });
- }
- private void bindShortcut(String name, KeyStroke keyStroke, final Runnable action) {
- getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keyStroke, name);
- getActionMap().put(name, new javax.swing.AbstractAction() {
- public void actionPerformed(ActionEvent e) {
- if (!(java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() instanceof JTextField)) {
- action.run();
- }
- }
- });
- }
- private JSONObject loadSchema() {
- if (designFile.exists()) {
- try {
- String text = new String(Files.readAllBytes(designFile.toPath()), StandardCharsets.UTF_8);
- JSONObject loaded = JSON.parseObject(text);
- if (loaded != null && loaded.getJSONArray("components") != null) {
- return loaded;
- }
- } catch (Exception e) {
- JOptionPane.showMessageDialog(this, "Failed to load UI design: " + e.getMessage());
- }
- }
- return sourceContainer == null ? emptySchema() : schemaFromContainer(sourceContainer);
- }
- private JSONObject emptySchema() {
- JSONObject root = new JSONObject();
- JSONObject canvasObject = new JSONObject();
- canvasObject.put("width", CANVAS_WIDTH);
- canvasObject.put("height", CANVAS_HEIGHT);
- canvasObject.put("gridSize", GRID_SIZE);
- root.put("version", 1);
- root.put("source", "empty");
- root.put("canvas", canvasObject);
- root.put("components", new JSONArray());
- return root;
- }
- private JSONObject schemaFromContainer(Container container) {
- JSONObject root = emptySchema();
- root.put("source", "runtime-swing-container");
- JSONArray components = root.getJSONArray("components");
- sourceComponents.clear();
- Component[] children = container.getComponents();
- for (int i = 0; i < children.length; i++) {
- Component child = children[i];
- if (!(child instanceof JComponent) || !child.isVisible()) {
- continue;
- }
- JSONObject item = componentToJson((JComponent) child, i);
- components.add(item);
- sourceComponents.put(item.getString("sourceId"), child);
- }
- return root;
- }
- private JSONObject componentToJson(JComponent component, int index) {
- Rectangle bounds = component.getBounds();
- String type = getComponentType(component);
- String sourceId = type + "-" + index;
- component.putClientProperty("visualUiEditor.sourceId", sourceId);
- JSONObject item = new JSONObject();
- JSONObject props = new JSONObject();
- item.put("id", sourceId);
- item.put("sourceId", sourceId);
- item.put("type", type);
- item.put("x", bounds.x);
- item.put("y", bounds.y);
- item.put("width", Math.max(bounds.width, 24));
- item.put("height", Math.max(bounds.height, 24));
- props.put("text", getComponentText(component));
- props.put("enabled", component.isEnabled());
- item.put("props", props);
- item.put("style", new JSONObject());
- return item;
- }
- public static void applySavedDesign(Container container) {
- JSONObject savedSchema = readSavedSchema();
- if (savedSchema == null || container == null) {
- return;
- }
- Map<String, Component> componentsBySourceId = new HashMap<String, Component>();
- indexSourceComponents(container, componentsBySourceId);
- applySchemaToContainer(savedSchema, container, componentsBySourceId);
- }
- private static JSONObject readSavedSchema() {
- File file = new File(System.getProperty("user.dir"), DESIGN_FILE);
- if (!file.exists()) {
- return null;
- }
- try {
- String text = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
- JSONObject loaded = JSON.parseObject(text);
- if (loaded != null && loaded.getJSONArray("components") != null) {
- return loaded;
- }
- } catch (Exception e) {
- System.err.println("Failed to load saved UI design: " + e.getMessage());
- }
- return null;
- }
- private static void indexSourceComponents(Container container, Map<String, Component> target) {
- target.clear();
- Component[] children = container.getComponents();
- for (int i = 0; i < children.length; i++) {
- Component child = children[i];
- if (!(child instanceof JComponent)) {
- continue;
- }
- JComponent component = (JComponent) child;
- String sourceId = (String) component.getClientProperty("visualUiEditor.sourceId");
- if (sourceId == null || sourceId.length() == 0) {
- sourceId = getComponentType(component) + "-" + i;
- component.putClientProperty("visualUiEditor.sourceId", sourceId);
- }
- target.put(sourceId, component);
- }
- }
- private static void applySchemaToContainer(JSONObject schema, Container container, Map<String, Component> componentsBySourceId) {
- Set<String> appliedSourceIds = new HashSet<String>();
- JSONArray components = schema.getJSONArray("components");
- for (int i = 0; i < components.size(); i++) {
- JSONObject item = components.getJSONObject(i);
- String sourceId = item.getString("sourceId");
- Component component = sourceId == null ? null : componentsBySourceId.get(sourceId);
- if (component == null) {
- String id = item.getString("id");
- component = id == null ? null : componentsBySourceId.get(id);
- }
- if (component == null) {
- component = createRuntimeComponent(item);
- String newSourceId = item.getString("id");
- if (newSourceId != null) {
- ((JComponent) component).putClientProperty("visualUiEditor.sourceId", newSourceId);
- componentsBySourceId.put(newSourceId, component);
- appliedSourceIds.add(newSourceId);
- }
- container.add(component);
- } else {
- String existingSourceId = (String) ((JComponent) component).getClientProperty("visualUiEditor.sourceId");
- if (existingSourceId != null) {
- appliedSourceIds.add(existingSourceId);
- }
- }
- component.setVisible(true);
- component.setBounds(item.getIntValue("x"), item.getIntValue("y"), item.getIntValue("width"), item.getIntValue("height"));
- applyText(component, item.getJSONObject("props").getString("text"));
- }
- for (Map.Entry<String, Component> entry : componentsBySourceId.entrySet()) {
- if (!appliedSourceIds.contains(entry.getKey())) {
- entry.getValue().setVisible(false);
- }
- }
- container.revalidate();
- container.repaint();
- }
- private static JComponent createRuntimeComponent(JSONObject item) {
- String type = item.getString("type");
- String text = item.getJSONObject("props").getString("text");
- if ("button".equals(type)) {
- return new JButton(text);
- }
- if ("textfield".equals(type)) {
- return new JTextField(text);
- }
- if ("panel".equals(type)) {
- JPanel panel = new JPanel(new BorderLayout());
- panel.add(new JLabel(text, SwingConstants.CENTER), BorderLayout.CENTER);
- panel.setBackground(new Color(230, 235, 240));
- return panel;
- }
- JLabel label = new JLabel(text, SwingConstants.CENTER);
- label.setOpaque(true);
- label.setBackground(Color.WHITE);
- return label;
- }
- private static String getComponentType(JComponent component) {
- if (component instanceof JButton) {
- return "button";
- }
- if (component instanceof JTextField) {
- return "textfield";
- }
- if (component instanceof JLabel) {
- return "label";
- }
- if (component instanceof JPanel) {
- return "panel";
- }
- return "component";
- }
- private static String getComponentText(JComponent component) {
- if (component instanceof JButton) {
- return ((JButton) component).getText();
- }
- if (component instanceof JTextField) {
- return ((JTextField) component).getText();
- }
- if (component instanceof JLabel) {
- return ((JLabel) component).getText();
- }
- return component.getClass().getSimpleName();
- }
- private void saveDesign() {
- try {
- File parent = designFile.getParentFile();
- if (parent != null && !parent.exists()) {
- parent.mkdirs();
- }
- Files.write(designFile.toPath(), JSON.toJSONString(schema).getBytes(StandardCharsets.UTF_8));
- dirty = false;
- applyToSourceContainer();
- updateStatus("Saved and applied " + designFile.getPath());
- } catch (IOException e) {
- JOptionPane.showMessageDialog(this, "Save failed: " + e.getMessage());
- }
- }
- private void importCurrentLayout() {
- if (sourceContainer == null) {
- return;
- }
- pushHistory();
- schema = schemaFromContainer(sourceContainer);
- selectedId = null;
- markDirty();
- renderAll();
- }
- private void applyToSourceContainer() {
- if (sourceContainer == null) {
- return;
- }
- indexSourceComponents(sourceContainer, sourceComponents);
- applySchemaToContainer(schema, sourceContainer, sourceComponents);
- updateStatus("Applied to current 工作面板 instance.");
- }
- private static void applyText(Component component, String text) {
- if (component instanceof JButton) {
- ((JButton) component).setText(text);
- } else if (component instanceof JTextField) {
- ((JTextField) component).setText(text);
- } else if (component instanceof JLabel) {
- ((JLabel) component).setText(text);
- }
- }
- private void addComponentAt(String type, int x, int y) {
- pushHistory();
- JSONObject item = new JSONObject();
- JSONObject props = new JSONObject();
- String id = type + "-" + System.currentTimeMillis();
- int width = defaultWidth(type);
- int height = defaultHeight(type);
- item.put("id", id);
- item.put("type", type);
- item.put("x", clamp(snap(x), 0, CANVAS_WIDTH - width));
- item.put("y", clamp(snap(y), 0, CANVAS_HEIGHT - height));
- item.put("width", width);
- item.put("height", height);
- props.put("text", defaultText(type));
- props.put("enabled", true);
- item.put("props", props);
- item.put("style", new JSONObject());
- schema.getJSONArray("components").add(item);
- selectedId = id;
- markDirty();
- renderAll();
- }
- private int defaultWidth(String type) {
- return "panel".equals(type) || "textfield".equals(type) ? 180 : 120;
- }
- private int defaultHeight(String type) {
- return "panel".equals(type) ? 120 : 36;
- }
- private String defaultText(String type) {
- if ("label".equals(type)) {
- return "Label";
- }
- if ("button".equals(type)) {
- return "Button";
- }
- if ("textfield".equals(type)) {
- return "Text";
- }
- return "Panel";
- }
- private void renderAll() {
- renderCanvas(canvas, true);
- updateInspector();
- updateStatus(null);
- }
- private void renderCanvas(JPanel target, boolean editable) {
- target.removeAll();
- JSONArray components = schema.getJSONArray("components");
- for (int i = 0; i < components.size(); i++) {
- JSONObject item = components.getJSONObject(i);
- JComponent view = createView(item, editable);
- view.setBounds(item.getIntValue("x"), item.getIntValue("y"), item.getIntValue("width"), item.getIntValue("height"));
- target.add(view);
- }
- target.revalidate();
- target.repaint();
- }
- private JComponent createView(final JSONObject item, boolean editable) {
- String type = item.getString("type");
- String text = item.getJSONObject("props").getString("text");
- JComponent view;
- if ("button".equals(type)) {
- view = new DesignerButton(text, editable && selectedId != null && selectedId.equals(item.getString("id")));
- } else if ("textfield".equals(type)) {
- JTextField field = new DesignerTextField(text, editable && selectedId != null && selectedId.equals(item.getString("id")));
- field.setEditable(false);
- view = field;
- } else if ("panel".equals(type)) {
- JPanel panel = new DesignerPanel(editable && selectedId != null && selectedId.equals(item.getString("id")));
- panel.setLayout(new BorderLayout());
- panel.add(new JLabel(text, SwingConstants.CENTER), BorderLayout.CENTER);
- panel.setBackground(new Color(230, 235, 240));
- view = panel;
- } else {
- JLabel label = new DesignerLabel(text, editable && selectedId != null && selectedId.equals(item.getString("id")));
- label.setOpaque(true);
- label.setBackground(Color.WHITE);
- view = label;
- }
- view.setFont(new Font("Dialog", Font.PLAIN, 16));
- view.setEnabled(editable || item.getJSONObject("props").getBooleanValue("enabled"));
- view.setBorder(BorderFactory.createLineBorder(selectedId != null && selectedId.equals(item.getString("id")) && editable ? Color.BLUE : Color.GRAY, editable ? 2 : 1));
- if (editable) {
- installComponentEditing(view, item);
- }
- return view;
- }
- private void installComponentEditing(final JComponent view, final JSONObject item) {
- final String id = item.getString("id");
- MouseAdapter adapter = new MouseAdapter() {
- private Point startPoint;
- private Rectangle startBounds;
- private String resizeHandle;
- private boolean changed;
- public void mousePressed(MouseEvent e) {
- e.consume();
- selectedId = id;
- startPoint = SwingUtilities.convertPoint(view, e.getPoint(), canvas);
- startBounds = view.getBounds();
- resizeHandle = getResizeHandle(e.getPoint(), view);
- changed = false;
- pushHistory();
- updateInspector();
- view.setBorder(BorderFactory.createLineBorder(Color.BLUE, 2));
- canvas.repaint();
- canvas.requestFocusInWindow();
- }
- public void mouseDragged(MouseEvent e) {
- Point current = SwingUtilities.convertPoint(view, e.getPoint(), canvas);
- int dx = current.x - startPoint.x;
- int dy = current.y - startPoint.y;
- int nextX = startBounds.x;
- int nextY = startBounds.y;
- int nextWidth = startBounds.width;
- int nextHeight = startBounds.height;
- if (resizeHandle != null) {
- Rectangle resized = resizeBounds(startBounds, dx, dy, resizeHandle);
- nextX = resized.x;
- nextY = resized.y;
- nextWidth = resized.width;
- nextHeight = resized.height;
- } else {
- nextX = clamp(snap(startBounds.x + dx), 0, CANVAS_WIDTH - startBounds.width);
- nextY = clamp(snap(startBounds.y + dy), 0, CANVAS_HEIGHT - startBounds.height);
- }
- item.put("x", nextX);
- item.put("y", nextY);
- item.put("width", nextWidth);
- item.put("height", nextHeight);
- view.setBounds(nextX, nextY, nextWidth, nextHeight);
- changed = true;
- markDirty();
- updateInspector();
- canvas.repaint();
- }
- public void mouseReleased(MouseEvent e) {
- if (!changed && !undoStack.isEmpty()) {
- undoStack.pop();
- } else if (changed) {
- renderAll();
- }
- }
- public void mouseMoved(MouseEvent e) {
- view.setCursor(cursorForHandle(getResizeHandle(e.getPoint(), view)));
- }
- };
- view.addMouseListener(adapter);
- view.addMouseMotionListener(adapter);
- }
- private String getResizeHandle(Point point, JComponent component) {
- int w = component.getWidth();
- int h = component.getHeight();
- boolean left = point.x <= HANDLE_SIZE;
- boolean right = point.x >= w - HANDLE_SIZE;
- boolean top = point.y <= HANDLE_SIZE;
- boolean bottom = point.y >= h - HANDLE_SIZE;
- boolean middleX = point.x >= (w / 2) - HANDLE_SIZE && point.x <= (w / 2) + HANDLE_SIZE;
- boolean middleY = point.y >= (h / 2) - HANDLE_SIZE && point.y <= (h / 2) + HANDLE_SIZE;
- if (left && top) {
- return "nw";
- }
- if (right && top) {
- return "ne";
- }
- if (left && bottom) {
- return "sw";
- }
- if (right && bottom) {
- return "se";
- }
- if (middleX && top) {
- return "n";
- }
- if (middleX && bottom) {
- return "s";
- }
- if (left && middleY) {
- return "w";
- }
- if (right && middleY) {
- return "e";
- }
- return null;
- }
- private Rectangle resizeBounds(Rectangle startBounds, int dx, int dy, String handle) {
- int minSize = 24;
- int x = startBounds.x;
- int y = startBounds.y;
- int width = startBounds.width;
- int height = startBounds.height;
- if (handle.indexOf("e") >= 0) {
- width = clamp(snap(startBounds.width + dx), minSize, CANVAS_WIDTH - startBounds.x);
- }
- if (handle.indexOf("s") >= 0) {
- height = clamp(snap(startBounds.height + dy), minSize, CANVAS_HEIGHT - startBounds.y);
- }
- if (handle.indexOf("w") >= 0) {
- int newX = clamp(snap(startBounds.x + dx), 0, startBounds.x + startBounds.width - minSize);
- width = startBounds.x + startBounds.width - newX;
- x = newX;
- }
- if (handle.indexOf("n") >= 0) {
- int newY = clamp(snap(startBounds.y + dy), 0, startBounds.y + startBounds.height - minSize);
- height = startBounds.y + startBounds.height - newY;
- y = newY;
- }
- return new Rectangle(x, y, width, height);
- }
- private Cursor cursorForHandle(String handle) {
- if (handle == null) {
- return Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
- }
- if ("n".equals(handle) || "s".equals(handle)) {
- return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
- }
- if ("e".equals(handle) || "w".equals(handle)) {
- return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
- }
- if ("ne".equals(handle) || "sw".equals(handle)) {
- return Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
- }
- return Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
- }
- private int snap(int value) {
- return Math.round(value / (float) GRID_SIZE) * GRID_SIZE;
- }
- private int clamp(int value, int min, int max) {
- return Math.max(min, Math.min(max, value));
- }
- private void applyInspector() {
- JSONObject item = selectedItem();
- if (item == null) {
- return;
- }
- pushHistory();
- try {
- item.getJSONObject("props").put("text", textField.getText());
- item.put("x", clamp(snap(Integer.parseInt(xField.getText())), 0, CANVAS_WIDTH));
- item.put("y", clamp(snap(Integer.parseInt(yField.getText())), 0, CANVAS_HEIGHT));
- item.put("width", clamp(snap(Integer.parseInt(widthField.getText())), 24, CANVAS_WIDTH));
- item.put("height", clamp(snap(Integer.parseInt(heightField.getText())), 24, CANVAS_HEIGHT));
- markDirty();
- renderAll();
- } catch (NumberFormatException e) {
- if (!undoStack.isEmpty()) {
- undoStack.pop();
- }
- JOptionPane.showMessageDialog(this, "Position and size must be numbers.");
- updateInspector();
- }
- }
- private void deleteSelected() {
- if (selectedId == null) {
- return;
- }
- pushHistory();
- JSONArray components = schema.getJSONArray("components");
- for (int i = components.size() - 1; i >= 0; i--) {
- if (selectedId.equals(components.getJSONObject(i).getString("id"))) {
- components.remove(i);
- }
- }
- selectedId = null;
- markDirty();
- renderAll();
- }
- private JSONObject selectedItem() {
- if (selectedId == null) {
- return null;
- }
- JSONArray components = schema.getJSONArray("components");
- for (int i = 0; i < components.size(); i++) {
- JSONObject item = components.getJSONObject(i);
- if (selectedId.equals(item.getString("id"))) {
- return item;
- }
- }
- return null;
- }
- private void updateInspector() {
- JSONObject item = selectedItem();
- boolean enabled = item != null;
- textField.setEnabled(enabled);
- xField.setEnabled(enabled);
- yField.setEnabled(enabled);
- widthField.setEnabled(enabled);
- heightField.setEnabled(enabled);
- if (!enabled) {
- textField.setText("");
- xField.setText("");
- yField.setText("");
- widthField.setText("");
- heightField.setText("");
- return;
- }
- textField.setText(item.getJSONObject("props").getString("text"));
- xField.setText(String.valueOf(item.getIntValue("x")));
- yField.setText(String.valueOf(item.getIntValue("y")));
- widthField.setText(String.valueOf(item.getIntValue("width")));
- heightField.setText(String.valueOf(item.getIntValue("height")));
- }
- private void pushHistory() {
- undoStack.push(JSON.toJSONString(schema));
- redoStack.clear();
- }
- private void undo() {
- if (undoStack.isEmpty()) {
- return;
- }
- redoStack.push(JSON.toJSONString(schema));
- schema = JSON.parseObject(undoStack.pop());
- selectedId = null;
- markDirty();
- renderAll();
- }
- private void redo() {
- if (redoStack.isEmpty()) {
- return;
- }
- undoStack.push(JSON.toJSONString(schema));
- schema = JSON.parseObject(redoStack.pop());
- selectedId = null;
- markDirty();
- renderAll();
- }
- private void markDirty() {
- dirty = true;
- }
- private void updateStatus(String message) {
- if (message != null) {
- statusLabel.setText(message);
- return;
- }
- statusLabel.setText((dirty ? "Unsaved" : "Saved") + " - imported current 工作面板. Ctrl+S save, Ctrl+Z undo, Ctrl+Y redo. File: " + DESIGN_FILE);
- }
- private static void paintHandles(Component component, Graphics graphics, boolean selected) {
- if (!selected) {
- return;
- }
- graphics.setColor(Color.BLUE);
- int s = HANDLE_SIZE;
- int half = s / 2;
- int w = component.getWidth();
- int h = component.getHeight();
- int[][] points = new int[][]{
- {0, 0}, {w / 2, 0}, {w, 0},
- {0, h / 2}, {w, h / 2},
- {0, h}, {w / 2, h}, {w, h}
- };
- for (int i = 0; i < points.length; i++) {
- graphics.fillRect(points[i][0] - half, points[i][1] - half, s, s);
- }
- }
- private static class DesignerButton extends JButton {
- private final boolean selected;
- DesignerButton(String text, boolean selected) {
- super(text);
- this.selected = selected;
- }
- protected void paintComponent(Graphics graphics) {
- super.paintComponent(graphics);
- paintHandles(this, graphics, selected);
- }
- }
- private static class DesignerTextField extends JTextField {
- private final boolean selected;
- DesignerTextField(String text, boolean selected) {
- super(text);
- this.selected = selected;
- }
- protected void paintComponent(Graphics graphics) {
- super.paintComponent(graphics);
- paintHandles(this, graphics, selected);
- }
- }
- private static class DesignerLabel extends JLabel {
- private final boolean selected;
- DesignerLabel(String text, boolean selected) {
- super(text, SwingConstants.CENTER);
- this.selected = selected;
- }
- protected void paintComponent(Graphics graphics) {
- super.paintComponent(graphics);
- paintHandles(this, graphics, selected);
- }
- }
- private static class DesignerPanel extends JPanel {
- private final boolean selected;
- DesignerPanel(boolean selected) {
- this.selected = selected;
- }
- protected void paintComponent(Graphics graphics) {
- super.paintComponent(graphics);
- paintHandles(this, graphics, selected);
- }
- }
- }
|