mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Render the Schematic tab with the real canvas (rulers, scroll, wheel-zoom)
Replace the custom SchematicPanel-in-a-JScrollPane in the tab with a second Presenter + CanvasPlugin, so the Schematic tab uses the exact same RulerScrollPane, scroll bars and wheel/gesture zoom as the Layout tab. The tab strip now has only the two tabs - no Refresh or zoom buttons. Switching to Schematic runs SchematicSynchronizer automatically and loadProject()s the wrapper into the schematic presenter; switching to Layout just toggles which scroll pane is visible in the center panel. SchematicPanel / SchematicViewFrame / SchematicMenuPlugin stay as the independent detached-window viewer (Analyze > Schematic View...). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWD3QJxbr7RuEHxyFEYeEY
This commit is contained in:
@@ -147,8 +147,8 @@ public class MainFrame extends JFrame implements ISwingUI {
|
||||
presenter.installPlugin(() -> canvasPlugin);
|
||||
presenter.installPlugin(() -> new ComponentTree(this, canvasPlugin.getCanvasPanel()));
|
||||
presenter.installPlugin(() -> new ExplorerPlugin(this, canvasPlugin.getCanvasPanel()));
|
||||
presenter.installPlugin(
|
||||
() -> new org.diylc.swing.plugins.schematic.SchematicTabPlugin(this, canvasPlugin));
|
||||
presenter.installPlugin(() -> new org.diylc.swing.plugins.schematic.SchematicTabPlugin(this,
|
||||
configManager, canvasPlugin));
|
||||
|
||||
presenter.installPlugin(() -> new FramePlugin());
|
||||
|
||||
|
||||
+75
-69
@@ -26,39 +26,45 @@ import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.appframework.miscutils.IConfigurationManager;
|
||||
import org.diylc.common.BadPositionException;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.IPlugIn;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.SchematicView;
|
||||
import org.diylc.presenter.ContinuityArea;
|
||||
import org.diylc.presenter.Presenter;
|
||||
import org.diylc.schematic.SchematicSynchronizer;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
import org.diylc.swing.plugins.canvas.CanvasPlugin;
|
||||
|
||||
/**
|
||||
* Adds an Excel-style tab strip below the canvas with two tabs, <b>Layout</b> and <b>Schematic</b>.
|
||||
* Selecting the Schematic tab regenerates the schematic from the current layout and swaps the canvas
|
||||
* area for a read-only {@link SchematicPanel}; selecting Layout swaps it back.
|
||||
*
|
||||
* <p>
|
||||
* Editing schematic symbols in place (drag / rotate / mirror with shared undo) via a restricted
|
||||
* {@code Presenter} is a planned follow-up; for now the schematic tab is a viewer with zoom, refresh
|
||||
* and export.
|
||||
* The schematic tab reuses the regular canvas UI: its own {@link Presenter} + {@link CanvasPlugin}
|
||||
* (the same {@code RulerScrollPane}, rulers, scroll bars and zoom-scroll) render a wrapper
|
||||
* {@link Project} backed by the layout's {@link SchematicView}. Switching to the Schematic tab
|
||||
* regenerates the schematic automatically ({@link SchematicSynchronizer}) and shows the schematic
|
||||
* canvas; switching to Layout shows the layout canvas again.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The schematic canvas is not restricted yet, so nothing prevents selecting or nudging symbols; a
|
||||
* dedicated restricted mode (no add / delete / paste, shared undo) is a planned follow-up.
|
||||
* </p>
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
@@ -68,31 +74,37 @@ public class SchematicTabPlugin implements IPlugIn {
|
||||
private static final Logger LOG = Logger.getLogger(SchematicTabPlugin.class);
|
||||
|
||||
private final ISwingUI swingUI;
|
||||
private final CanvasPlugin canvasPlugin;
|
||||
private final IConfigurationManager<?> configManager;
|
||||
private final CanvasPlugin layoutCanvasPlugin;
|
||||
|
||||
private IPlugInPort plugInPort;
|
||||
private Presenter schematicPresenter;
|
||||
private CanvasPlugin schematicCanvasPlugin;
|
||||
private JComponent layoutScroll;
|
||||
private JComponent schematicScroll;
|
||||
private JToggleButton schematicTab;
|
||||
|
||||
private final SchematicPanel schematicPanel = new SchematicPanel();
|
||||
private final List<JButton> schematicTools = new ArrayList<JButton>();
|
||||
private JScrollPane schematicScroll;
|
||||
private JComponent canvasScroll;
|
||||
|
||||
public SchematicTabPlugin(ISwingUI swingUI, CanvasPlugin canvasPlugin) {
|
||||
public SchematicTabPlugin(ISwingUI swingUI, IConfigurationManager<?> configManager,
|
||||
CanvasPlugin layoutCanvasPlugin) {
|
||||
this.swingUI = swingUI;
|
||||
this.canvasPlugin = canvasPlugin;
|
||||
this.configManager = configManager;
|
||||
this.layoutCanvasPlugin = layoutCanvasPlugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(IPlugInPort plugInPort) {
|
||||
this.plugInPort = plugInPort;
|
||||
this.canvasScroll = canvasPlugin.getCanvasScrollComponent();
|
||||
this.layoutScroll = layoutCanvasPlugin.getCanvasScrollComponent();
|
||||
|
||||
schematicScroll = new JScrollPane(schematicPanel);
|
||||
schematicScroll.getVerticalScrollBar().setUnitIncrement(16);
|
||||
schematicScroll.getHorizontalScrollBar().setUnitIncrement(16);
|
||||
schematicScroll.setVisible(false);
|
||||
// A second presenter + canvas plugin gives the schematic the exact same rendering, scrolling
|
||||
// and zoom behaviour as the layout canvas.
|
||||
this.schematicPresenter = new Presenter(swingUI, configManager, false);
|
||||
this.schematicCanvasPlugin = new CanvasPlugin(swingUI, configManager);
|
||||
this.schematicPresenter.installPlugin(() -> schematicCanvasPlugin);
|
||||
this.schematicScroll = schematicCanvasPlugin.getCanvasScrollComponent();
|
||||
this.schematicScroll.setVisible(false);
|
||||
|
||||
try {
|
||||
swingUI.injectGUIComponent(schematicScroll, SwingConstants.CENTER, false, null);
|
||||
swingUI.injectGUIComponent(buildTabBar(), SwingConstants.CENTER, false, null);
|
||||
} catch (BadPositionException e) {
|
||||
LOG.error("Could not install schematic tab", e);
|
||||
@@ -107,19 +119,12 @@ public class SchematicTabPlugin implements IPlugIn {
|
||||
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
JToggleButton layoutTab = makeTab("Layout", true, e -> showLayout());
|
||||
JToggleButton schematicTab = makeTab("Schematic", false, e -> showSchematic());
|
||||
schematicTab = makeTab("Schematic", false, e -> showSchematic());
|
||||
group.add(layoutTab);
|
||||
group.add(schematicTab);
|
||||
bar.add(layoutTab);
|
||||
bar.add(schematicTab);
|
||||
|
||||
bar.add(Box.createHorizontalStrut(16));
|
||||
schematicTools.add(addTool(bar, "Refresh", e -> refreshSchematic(true)));
|
||||
schematicTools.add(addTool(bar, "Zoom −", e -> schematicPanel.setZoom(schematicPanel.getZoom() / 1.25d)));
|
||||
schematicTools.add(addTool(bar, "Zoom +", e -> schematicPanel.setZoom(schematicPanel.getZoom() * 1.25d)));
|
||||
schematicTools.add(addTool(bar, "Reset Zoom", e -> schematicPanel.setZoom(1d)));
|
||||
setToolsEnabled(false);
|
||||
|
||||
Dimension pref = bar.getPreferredSize();
|
||||
bar.setMaximumSize(new Dimension(Integer.MAX_VALUE, pref.height));
|
||||
bar.setMinimumSize(new Dimension(0, pref.height));
|
||||
@@ -134,55 +139,56 @@ public class SchematicTabPlugin implements IPlugIn {
|
||||
return button;
|
||||
}
|
||||
|
||||
private static JButton addTool(JToolBar bar, String label, ActionListener listener) {
|
||||
JButton button = new JButton(new AbstractAction(label) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
listener.actionPerformed(e);
|
||||
}
|
||||
});
|
||||
button.setFocusPainted(false);
|
||||
bar.add(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
private void setToolsEnabled(boolean enabled) {
|
||||
for (JButton button : schematicTools) {
|
||||
button.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void showLayout() {
|
||||
schematicScroll.setVisible(false);
|
||||
canvasScroll.setVisible(true);
|
||||
setToolsEnabled(false);
|
||||
layoutScroll.setVisible(true);
|
||||
relayout();
|
||||
}
|
||||
|
||||
private void showSchematic() {
|
||||
refreshSchematic(false);
|
||||
canvasScroll.setVisible(false);
|
||||
schematicScroll.setVisible(true);
|
||||
setToolsEnabled(true);
|
||||
relayout();
|
||||
}
|
||||
|
||||
private void refreshSchematic(boolean reportErrors) {
|
||||
Project wrapper;
|
||||
try {
|
||||
schematicPanel.refresh(plugInPort.getCurrentProject(), plugInPort.getContinuityAreas());
|
||||
wrapper = buildSchematicProject();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not build schematic view", e);
|
||||
if (reportErrors) {
|
||||
swingUI.showMessage("Could not build the schematic view: " + e.getMessage(), "Schematic View",
|
||||
ISwingUI.ERROR_MESSAGE);
|
||||
}
|
||||
swingUI.showMessage("Could not build the schematic view: " + e.getMessage(), "Schematic View",
|
||||
ISwingUI.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
schematicPresenter.loadProject(wrapper, true, null);
|
||||
layoutScroll.setVisible(false);
|
||||
schematicScroll.setVisible(true);
|
||||
relayout();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
schematicCanvasPlugin.scrollToCenterAndShowContents();
|
||||
relayout();
|
||||
});
|
||||
}
|
||||
|
||||
/** Regenerates the schematic from the current layout and wraps it in a renderable project. */
|
||||
private Project buildSchematicProject() {
|
||||
Project layoutProject = plugInPort.getCurrentProject();
|
||||
java.util.List<ContinuityArea> areas = plugInPort.getContinuityAreas();
|
||||
new SchematicSynchronizer().synchronize(layoutProject,
|
||||
areas == null ? new java.util.ArrayList<>() : areas);
|
||||
|
||||
SchematicView view = layoutProject.getOrCreateSchematicView();
|
||||
Project wrapper = new Project();
|
||||
wrapper.setTitle(layoutProject.getTitle() + " — Schematic");
|
||||
wrapper.setAuthor(layoutProject.getAuthor());
|
||||
wrapper.setWidth(view.getWidth());
|
||||
wrapper.setHeight(view.getHeight());
|
||||
wrapper.setGridSpacing(view.getGridSpacing());
|
||||
wrapper.setFont(layoutProject.getFont());
|
||||
wrapper.getComponents().addAll(view.getComponents());
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private void relayout() {
|
||||
JComponent parent = (JComponent) canvasScroll.getParent();
|
||||
JComponent parent = layoutScroll.getParent() instanceof JComponent
|
||||
? (JComponent) layoutScroll.getParent()
|
||||
: null;
|
||||
if (parent != null) {
|
||||
parent.revalidate();
|
||||
parent.repaint();
|
||||
|
||||
@@ -440,15 +440,14 @@ The schematic uses the **existing `DrawingManager` + `Presenter`** rendering pip
|
||||
- Concrete factories: `Resistor`, `Capacitor`, `Diode`, `Transistor` (→ `BJTSymbol`), `Inductor`, `Potentiometer`, wired onto the common physical component types (resistors, film/ceramic/electrolytic/tantalum/mica caps, plastic/glass diodes, TO-1/92/126/220/3 transistors, radial/toroidal inductors, panel/trimmer/miniature pots).
|
||||
|
||||
**diylc-swing**
|
||||
- `SchematicTabPlugin` adds the Excel-style tab strip (**Layout | Schematic**) below the canvas. Selecting **Schematic** runs `SchematicSynchronizer` and swaps the canvas area for a read-only `SchematicPanel` (with Refresh / Zoom controls in the tab strip); selecting **Layout** swaps it back. `CanvasPlugin.getCanvasScrollComponent()` exposes the layout scroll pane so the plugin can show/hide it.
|
||||
- `SchematicMenuPlugin` adds *Analyze → Schematic View…*, which opens the same viewer in a detached `SchematicViewFrame` window (adds PNG export).
|
||||
- `SchematicPanel` renders the generated `SchematicView` through the existing `DrawingManager` pipeline.
|
||||
- `SchematicTabPlugin` adds the Excel-style tab strip (**Layout | Schematic**) below the canvas. It owns a **second `Presenter` + `CanvasPlugin`**, so the schematic renders through the exact same `RulerScrollPane` — rulers, scroll bars, wheel/gesture zoom — as the layout canvas, with no extra controls. Selecting **Schematic** runs `SchematicSynchronizer` automatically and loads the resulting wrapper `Project` into the schematic presenter; selecting **Layout** swaps the visible scroll pane back. `CanvasPlugin.getCanvasScrollComponent()` exposes each scroll pane so the plugin can show/hide it.
|
||||
- `SchematicMenuPlugin` / `SchematicViewFrame` / `SchematicPanel` — a detached-window viewer (*Analyze → Schematic View…*) that renders the `SchematicView` through a private `DrawingManager` and adds PNG export. Independent of the tab.
|
||||
|
||||
Unit tests: `ManhattanRouterTest`, `GenericBoxSchematicFactoryTest` (library); `SchematicViewIntegrationTest` (swing — end-to-end build/sync/render).
|
||||
|
||||
### Not yet done (follow-ups)
|
||||
|
||||
- The schematic tab is **read-only**. Making it editable needs a **restricted second `Presenter`** wired into a real canvas (drag / multi-select / rotate / flip on symbols, drag-to-move re-routing on mouse release).
|
||||
- The schematic canvas is not **restricted** yet: selecting and nudging symbols is possible, but there is no explicit block on add / delete / paste / value-edit, and no shared undo integration with the layout history.
|
||||
- Shared undo/redo integration with the layout history.
|
||||
- Multi-section 1:N factories (`TubeSocket` → triode/pentode/duo-diode, `DIL_IC` → dual op-amps, switches).
|
||||
- Smarter placement (true connection-density clustering) and crossing-aware routing.
|
||||
|
||||
Reference in New Issue
Block a user