Align and Distribute

This commit is contained in:
Bane Stojkovic
2026-10-09 23:18:02 +02:00
parent cd9ac32a2f
commit 33b614c7ff
29 changed files with 1018 additions and 9 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>org.diylc</groupId>
<artifactId>diylc-parent</artifactId>
<version>6.7.0</version>
<version>6.8.0</version>
</parent>
<artifactId>diylc-swing</artifactId>
@@ -24,6 +24,8 @@ import org.apache.log4j.Logger;
import org.diylc.swing.actions.analyze.*;
import org.diylc.swingframework.IDrawingProvider;
import org.diylc.common.AlignmentMode;
import org.diylc.common.DistributionMode;
import org.diylc.common.INetlistAnalyzer;
import org.diylc.common.IPlugInPort;
import org.diylc.core.ExpansionMode;
@@ -40,10 +42,12 @@ import org.diylc.swing.actions.RenumberAction;
import org.diylc.swing.actions.ResetCategoriesAction;
import org.diylc.swing.actions.ThemeAction;
import org.diylc.swing.actions.ToggleAction;
import org.diylc.swing.actions.edit.AlignSelectionAction;
import org.diylc.swing.actions.edit.BringToFrontAction;
import org.diylc.swing.actions.edit.CopyAction;
import org.diylc.swing.actions.edit.CutAction;
import org.diylc.swing.actions.edit.DeleteSelectionAction;
import org.diylc.swing.actions.edit.DistributeSelectionAction;
import org.diylc.swing.actions.edit.DuplicateAction;
import org.diylc.swing.actions.edit.EditProjectAction;
import org.diylc.swing.actions.edit.EditSelectionAction;
@@ -233,6 +237,16 @@ public class ActionFactory {
return new NudgeAction(plugInPort);
}
public AlignSelectionAction createAlignSelectionAction(IPlugInPort plugInPort,
AlignmentMode mode) {
return new AlignSelectionAction(plugInPort, mode);
}
public DistributeSelectionAction createDistributeSelectionAction(IPlugInPort plugInPort,
DistributionMode mode) {
return new DistributeSelectionAction(plugInPort, mode);
}
public FindAction createFindAction(IPlugInPort plugInPort, ISwingUI swingUI) {
return new FindAction(plugInPort, swingUI);
}
@@ -0,0 +1,69 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.swing.actions.edit;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.diylc.common.AlignmentMode;
import org.diylc.common.IPlugInPort;
import org.diylc.swing.ActionFactory;
import org.diylc.utils.IconLoader;
public class AlignSelectionAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private IPlugInPort plugInPort;
private AlignmentMode mode;
public AlignSelectionAction(IPlugInPort plugInPort, AlignmentMode mode) {
super();
this.plugInPort = plugInPort;
this.mode = mode;
putValue(AbstractAction.NAME, mode.toString());
putValue(AbstractAction.SMALL_ICON, getIcon(mode).getIcon());
}
@Override
public void actionPerformed(ActionEvent e) {
ActionFactory.LOG.info("Align Selection triggered: " + mode);
plugInPort.alignSelection(mode);
}
private static IconLoader getIcon(AlignmentMode mode) {
switch (mode) {
case LEFT:
return IconLoader.AlignLeft;
case RIGHT:
return IconLoader.AlignRight;
case TOP:
return IconLoader.AlignTop;
case BOTTOM:
return IconLoader.AlignBottom;
case HORIZONTAL_CENTER:
return IconLoader.AlignCenterHorizontal;
default:
return IconLoader.AlignCenterVertical;
}
}
}
@@ -0,0 +1,65 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.swing.actions.edit;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.diylc.common.DistributionMode;
import org.diylc.common.IPlugInPort;
import org.diylc.swing.ActionFactory;
import org.diylc.utils.IconLoader;
public class DistributeSelectionAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private IPlugInPort plugInPort;
private DistributionMode mode;
public DistributeSelectionAction(IPlugInPort plugInPort, DistributionMode mode) {
super();
this.plugInPort = plugInPort;
this.mode = mode;
putValue(AbstractAction.NAME, mode.toString());
putValue(AbstractAction.SMALL_ICON, getIcon(mode).getIcon());
}
@Override
public void actionPerformed(ActionEvent e) {
ActionFactory.LOG.info("Distribute Selection triggered: " + mode);
plugInPort.distributeSelection(mode);
}
private static IconLoader getIcon(DistributionMode mode) {
switch (mode) {
case HORIZONTAL_CENTERS:
return IconLoader.DistributeCentersHorizontal;
case HORIZONTAL_SPACING:
return IconLoader.DistributeSpacingHorizontal;
case VERTICAL_CENTERS:
return IconLoader.DistributeCentersVertical;
default:
return IconLoader.DistributeSpacingVertical;
}
}
}
@@ -30,7 +30,9 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JMenu;
@@ -39,18 +41,23 @@ import javax.swing.JPopupMenu;
import org.apache.log4j.Logger;
import org.diylc.clipboard.ComponentTransferable;
import org.diylc.common.AlignmentMode;
import org.diylc.common.DistributionMode;
import org.diylc.common.ComponentType;
import org.diylc.common.IComponentTransformer;
import org.diylc.common.IPlugInPort;
import org.diylc.core.ExpansionMode;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Template;
import org.diylc.presenter.AlignmentManager;
import org.diylc.swing.ActionFactory;
import org.diylc.swing.actions.FlexibleLeadsAction;
import org.diylc.swing.actions.edit.AlignSelectionAction;
import org.diylc.swing.actions.edit.BringToFrontAction;
import org.diylc.swing.actions.edit.CopyAction;
import org.diylc.swing.actions.edit.CutAction;
import org.diylc.swing.actions.edit.DeleteSelectionAction;
import org.diylc.swing.actions.edit.DistributeSelectionAction;
import org.diylc.swing.actions.edit.DuplicateAction;
import org.diylc.swing.actions.edit.EditSelectionAction;
import org.diylc.swing.actions.edit.ExpandSelectionAction;
@@ -75,6 +82,7 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
private JMenu selectionMenu;
private JMenu expandMenu;
private JMenu transformMenu;
private JMenu alignMenu;
private JMenu applyTemplateMenu;
private JMenu applyModelMenu;
private JMenu lockMenu;
@@ -101,6 +109,10 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
private MirrorSelectionAction mirrorHorizontallyAction;
private MirrorSelectionAction mirrorVerticallyAction;
private FlexibleLeadsAction flexibleLeadsAction;
private Map<AlignmentMode, AlignSelectionAction> alignActions =
new EnumMap<AlignmentMode, AlignSelectionAction>(AlignmentMode.class);
private Map<DistributionMode, DistributeSelectionAction> distributeActions =
new EnumMap<DistributionMode, DistributeSelectionAction>(DistributionMode.class);
private IPlugInPort plugInPort;
private Clipboard clipboard;
@@ -124,6 +136,7 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
add(getEditSelectionAction());
add(getDeleteSelectionAction());
add(getTransformMenu());
add(getAlignMenu());
add(getSaveAsTemplateAction());
add(getApplyTemplateMenu());
add(getApplyModelMenu());
@@ -194,6 +207,21 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
return transformMenu;
}
public JMenu getAlignMenu() {
if (alignMenu == null) {
alignMenu = new TranslatedMenu("Align & Distribute");
alignMenu.setIcon(IconLoader.AlignDistribute.getIcon());
for (AlignmentMode mode : AlignmentMode.values()) {
alignMenu.add(getAlignAction(mode));
}
alignMenu.addSeparator();
for (DistributionMode mode : DistributionMode.values()) {
alignMenu.add(getDistributeAction(mode));
}
}
return alignMenu;
}
public JMenu getApplyTemplateMenu() {
if (applyTemplateMenu == null) {
applyTemplateMenu = new TranslatedMenu("Apply Variant");
@@ -236,6 +264,13 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
getMirrorHorizontallyAction().setEnabled(enabled);
getMirrorVerticallyAction().setEnabled(enabled);
getFlexibleLeadsAction().setEnabled(enabled);
int unitCount = enabled ? plugInPort.getSelectionUnitCount() : 0;
for (AlignmentMode mode : AlignmentMode.values()) {
getAlignAction(mode).setEnabled(unitCount >= AlignmentManager.MIN_ALIGN_UNITS);
}
for (DistributionMode mode : DistributionMode.values()) {
getDistributeAction(mode).setEnabled(unitCount >= AlignmentManager.MIN_DISTRIBUTE_UNITS);
}
getSaveAsTemplateAction()
.setEnabled(plugInPort.getSelectedComponents().size() == 1);
@@ -429,6 +464,16 @@ public class ComponentPopupMenu extends JPopupMenu implements ClipboardOwner {
return flexibleLeadsAction;
}
public AlignSelectionAction getAlignAction(AlignmentMode mode) {
return alignActions.computeIfAbsent(mode,
m -> ActionFactory.getInstance().createAlignSelectionAction(plugInPort, m));
}
public DistributeSelectionAction getDistributeAction(DistributionMode mode) {
return distributeActions.computeIfAbsent(mode,
m -> ActionFactory.getInstance().createDistributeSelectionAction(plugInPort, m));
}
public SaveAsTemplateAction getSaveAsTemplateAction() {
if (saveAsTemplateAction == null) {
saveAsTemplateAction = ActionFactory.getInstance().createSaveAsTemplateAction(plugInPort);
@@ -27,26 +27,33 @@ import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.FlavorEvent;
import java.awt.datatransfer.FlavorListener;
import java.awt.datatransfer.Transferable;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import org.diylc.appframework.undo.IUndoListener;
import org.diylc.appframework.undo.UndoHandler;
import org.diylc.clipboard.ComponentTransferable;
import org.diylc.common.AlignmentMode;
import org.diylc.common.DistributionMode;
import org.diylc.common.EventType;
import org.diylc.common.IComponentTransformer;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.core.ExpansionMode;
import org.diylc.core.Project;
import org.diylc.presenter.AlignmentManager;
import org.diylc.swing.ActionFactory;
import org.diylc.swing.ISwingUI;
import org.diylc.swing.actions.FindAction;
import org.diylc.swing.actions.FlexibleLeadsAction;
import org.diylc.swing.actions.RenumberAction;
import org.diylc.swing.actions.edit.AlignSelectionAction;
import org.diylc.swing.actions.edit.BringToFrontAction;
import org.diylc.swing.actions.edit.CopyAction;
import org.diylc.swing.actions.edit.CutAction;
import org.diylc.swing.actions.edit.DeleteSelectionAction;
import org.diylc.swing.actions.edit.DistributeSelectionAction;
import org.diylc.swing.actions.edit.DuplicateAction;
import org.diylc.swing.actions.edit.EditSelectionAction;
import org.diylc.swing.actions.edit.ExpandSelectionAction;
@@ -65,6 +72,7 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner {
private static final String EDIT_TITLE = "Edit";
private static final String TRANSFORM_TITLE = "Transform Selection";
private static final String ALIGN_TITLE = "Align & Distribute";
private static final String RENUMBER_TITLE = "Renumber Selection";
private static final String EXPAND_TITLE = "Expand Selection";
@@ -97,6 +105,10 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner {
private MirrorSelectionAction mirrorVerticallyAction;
private FindAction findAction;
private FlexibleLeadsAction flexibleLeadsAction;
private Map<AlignmentMode, AlignSelectionAction> alignActions =
new EnumMap<AlignmentMode, AlignSelectionAction>(AlignmentMode.class);
private Map<DistributionMode, DistributeSelectionAction> distributeActions =
new EnumMap<DistributionMode, DistributeSelectionAction>(DistributionMode.class);
private UndoHandler<Project> undoHandler;
@@ -297,6 +309,16 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner {
return flexibleLeadsAction;
}
public AlignSelectionAction getAlignAction(AlignmentMode mode) {
return alignActions.computeIfAbsent(mode,
m -> ActionFactory.getInstance().createAlignSelectionAction(plugInPort, m));
}
public DistributeSelectionAction getDistributeAction(DistributionMode mode) {
return distributeActions.computeIfAbsent(mode,
m -> ActionFactory.getInstance().createDistributeSelectionAction(plugInPort, m));
}
@Override
public void connect(IPlugInPort plugInPort) {
this.plugInPort = plugInPort;
@@ -329,6 +351,14 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner {
swingUI.injectMenuAction(null, TRANSFORM_TITLE);
swingUI.injectMenuAction(getGroupAction(), TRANSFORM_TITLE);
swingUI.injectMenuAction(getUngroupAction(), TRANSFORM_TITLE);
swingUI.injectSubmenu(ALIGN_TITLE, IconLoader.AlignDistribute.getIcon(), EDIT_TITLE);
for (AlignmentMode mode : AlignmentMode.values()) {
swingUI.injectMenuAction(getAlignAction(mode), ALIGN_TITLE);
}
swingUI.injectMenuAction(null, ALIGN_TITLE);
for (DistributionMode mode : DistributionMode.values()) {
swingUI.injectMenuAction(getDistributeAction(mode), ALIGN_TITLE);
}
swingUI.injectMenuAction(null, EDIT_TITLE);
// swingUI.injectMenuAction(getSaveAsTemplateAction(), EDIT_TITLE);
swingUI.injectSubmenu(RENUMBER_TITLE, IconLoader.Sort.getIcon(), EDIT_TITLE);
@@ -395,6 +425,13 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner {
getSaveAsTemplateAction().setEnabled(enabled);
getSaveAsBlockAction().setEnabled(enabled);
getFlexibleLeadsAction().setEnabled(enabled);
int unitCount = enabled ? plugInPort.getSelectionUnitCount() : 0;
for (AlignmentMode mode : AlignmentMode.values()) {
getAlignAction(mode).setEnabled(unitCount >= AlignmentManager.MIN_ALIGN_UNITS);
}
for (DistributionMode mode : DistributionMode.values()) {
getDistributeAction(mode).setEnabled(unitCount >= AlignmentManager.MIN_DISTRIBUTE_UNITS);
}
}
// ClipboardOwner
@@ -36,8 +36,11 @@ import java.io.IOException;
*/
public enum IconLoader {
About("about.png"), Add("add.png"), ApplicationEdit("application_edit.png"), Arrow(
"arrow.png"), BOM("bom.png"), Back("back.png"), Barcode("barcode.png"), BlackBoard(
About("about.png"), Add("add.png"), AlignBottom("align_bottom.png"), AlignCenterHorizontal(
"align_center_horizontal.png"), AlignCenterVertical(
"align_center_vertical.png"), AlignDistribute("align_distribute.png"), AlignLeft(
"align_left.png"), AlignRight("align_right.png"), AlignTop(
"align_top.png"), ApplicationEdit("application_edit.png"), Arrow("arrow.png"), BOM("bom.png"), Back("back.png"), Barcode("barcode.png"), BlackBoard(
"blackboard.png"), Brain("brain.png"), BrainBig("brain_big.png"), BranchAdd(
"branch_add.png"), BriefcaseAdd("briefcase_add.png"), BriefcaseInto(
"briefcase_into.png"), Bug("bug.png"), CSV("csv.png"), Chest("chest.png"), Cloud(
@@ -49,7 +52,11 @@ public enum IconLoader {
"component_add.png"), ComponentLarge("component_large.png"), ComponentPreferences(
"component_preferences.png"), ComponentReplace("component_replace.png"), Copy(
"copy.png"), Cut("cut.png"), Dashboard("dashboard.png"), DataFind("data_find.png"), Delete(
"delete.png"), DiskBlue("disk_blue.png"), DocumentEdit("document_edit.png"), DocumentGerber(
"delete.png"), DiskBlue("disk_blue.png"), DistributeCentersHorizontal(
"distribute_centers_horizontal.png"), DistributeCentersVertical(
"distribute_centers_vertical.png"), DistributeSpacingHorizontal(
"distribute_spacing_horizontal.png"), DistributeSpacingVertical(
"distribute_spacing_vertical.png"), DocumentEdit("document_edit.png"), DocumentGerber(
"document_gerber.png"), DocumentInfo("document_info.png"), DocumentPlain(
"document_plain.png"), DocumentPlainYellow("document_plain_yellow.png"), DocumentX2(
"document_x2.png"), DocumentsGear("documents_gear.png"), Donate("donate.png"), Download(
Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B