Skip to content

Commit b93cf32

Browse files
authored
Added actions for aligning entities in the designer (#2577)
1 parent 57e1160 commit b93cf32

File tree

59 files changed

+2438
-140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2438
-140
lines changed

ugs-core/src/com/willwinder/universalgcodesender/utils/SwingHelpers.java

+101-99
Original file line numberDiff line numberDiff line change
@@ -3,117 +3,119 @@
33
import com.willwinder.universalgcodesender.i18n.Localization;
44
import com.willwinder.universalgcodesender.model.UnitUtils;
55
import com.willwinder.universalgcodesender.uielements.components.GcodeFileTypeFilter;
6-
import java.awt.Component;
7-
import java.awt.Container;
8-
import java.io.File;
9-
import java.util.Optional;
6+
107
import javax.swing.JComponent;
118
import javax.swing.JFileChooser;
129
import javax.swing.JFrame;
1310
import javax.swing.SpinnerNumberModel;
1411
import javax.swing.filechooser.FileFilter;
12+
import java.awt.Component;
13+
import java.awt.Container;
14+
import java.awt.Frame;
15+
import java.io.File;
16+
import java.util.Optional;
1517

1618
/**
17-
*
1819
* @author wwinder
1920
*/
2021
public class SwingHelpers {
21-
private static final String[] UNIT_OPTIONS = {
22-
Localization.getString("mainWindow.swing.mmRadioButton"),
23-
Localization.getString("mainWindow.swing.inchRadioButton")
24-
};
25-
26-
public static String[] getUnitOptions() {
27-
return UNIT_OPTIONS;
28-
}
29-
30-
public static UnitUtils.Units selectedUnit(int idx) {
31-
return idx == 0 ? UnitUtils.Units.MM : UnitUtils.Units.INCH;
32-
}
33-
34-
public static int unitIdx(UnitUtils.Units units) {
35-
if (units == UnitUtils.Units.INCH) {
36-
return 1;
37-
} else {
38-
return 0;
22+
private static final String[] UNIT_OPTIONS = {
23+
Localization.getString("mainWindow.swing.mmRadioButton"),
24+
Localization.getString("mainWindow.swing.inchRadioButton")
25+
};
26+
27+
public static String[] getUnitOptions() {
28+
return UNIT_OPTIONS;
3929
}
40-
}
41-
42-
// deal with casting the spinner model to a double.
43-
public static double getDouble(SpinnerNumberModel model) {
44-
return (double) model.getValue();
45-
}
46-
47-
// deal with casting the spinner model to a double.
48-
public static int getInt(SpinnerNumberModel model) {
49-
return (int) model.getValue();
50-
}
51-
52-
public static Optional<File> createFile(String sourceDir) {
53-
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
54-
int returnVal = fileChooser.showSaveDialog(new JFrame());
55-
if (returnVal == JFileChooser.APPROVE_OPTION) {
56-
return Optional.ofNullable(fileChooser.getSelectedFile());
57-
} else {
58-
return Optional.empty();
59-
}
60-
}
61-
62-
public static Optional<File> openFile(String sourceDir) {
63-
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
64-
int returnVal = fileChooser.showOpenDialog(new JFrame());
65-
if (returnVal == JFileChooser.APPROVE_OPTION) {
66-
return Optional.ofNullable(fileChooser.getSelectedFile());
67-
} else {
68-
return Optional.empty();
69-
}
70-
}
71-
72-
public static Optional<File> openDirectory(String title, File defaultDirectory) {
73-
JFileChooser chooser = new JFileChooser();
74-
if(defaultDirectory != null && defaultDirectory.isDirectory()) {
75-
chooser.setCurrentDirectory(defaultDirectory);
30+
31+
public static UnitUtils.Units selectedUnit(int idx) {
32+
return idx == 0 ? UnitUtils.Units.MM : UnitUtils.Units.INCH;
7633
}
77-
chooser.setDialogTitle(title);
78-
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
79-
chooser.setAcceptAllFileFilterUsed(false);
80-
chooser.setFileFilter(new FileFilter() {
81-
@Override
82-
public boolean accept(File f) {
83-
return f.isDirectory();
84-
}
85-
86-
@Override
87-
public String getDescription() {
88-
return "Directories";
89-
}
90-
});
91-
92-
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
93-
return Optional.of(chooser.getSelectedFile());
34+
35+
// deal with casting the spinner model to a double.
36+
public static double getDouble(SpinnerNumberModel model) {
37+
return (double) model.getValue();
9438
}
9539

96-
return Optional.empty();
97-
}
98-
99-
@FunctionalInterface
100-
public interface ApplyToComponent {
101-
public void apply(JComponent component);
102-
}
103-
104-
/**
105-
* Provides component hierarchy traversal.
106-
*
107-
* @param aContainer start node for the traversal.
108-
*/
109-
public static void traverse(Container aContainer, ApplyToComponent applicator) {
110-
for (final Component comp : aContainer.getComponents()) {
111-
if (comp instanceof JComponent) {
112-
applicator.apply((JComponent) comp);
113-
}
114-
if (comp instanceof Container) {
115-
traverse((Container) comp, applicator);
116-
}
117-
}
118-
}
40+
// deal with casting the spinner model to a double.
41+
public static int getInt(SpinnerNumberModel model) {
42+
return (int) model.getValue();
43+
}
44+
45+
public static Optional<File> createFile(String sourceDir) {
46+
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
47+
int returnVal = fileChooser.showSaveDialog(new JFrame());
48+
if (returnVal == JFileChooser.APPROVE_OPTION) {
49+
return Optional.ofNullable(fileChooser.getSelectedFile());
50+
} else {
51+
return Optional.empty();
52+
}
53+
}
54+
55+
public static Optional<File> openFile(String sourceDir) {
56+
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
57+
int returnVal = fileChooser.showOpenDialog(new JFrame());
58+
if (returnVal == JFileChooser.APPROVE_OPTION) {
59+
return Optional.ofNullable(fileChooser.getSelectedFile());
60+
} else {
61+
return Optional.empty();
62+
}
63+
}
64+
65+
public static Optional<File> openDirectory(String title, File defaultDirectory) {
66+
JFileChooser chooser = new JFileChooser();
67+
if (defaultDirectory != null && defaultDirectory.isDirectory()) {
68+
chooser.setCurrentDirectory(defaultDirectory);
69+
}
70+
chooser.setDialogTitle(title);
71+
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
72+
chooser.setAcceptAllFileFilterUsed(false);
73+
chooser.setFileFilter(new FileFilter() {
74+
@Override
75+
public boolean accept(File f) {
76+
return f.isDirectory();
77+
}
78+
79+
@Override
80+
public String getDescription() {
81+
return "Directories";
82+
}
83+
});
84+
85+
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
86+
return Optional.of(chooser.getSelectedFile());
87+
}
88+
89+
return Optional.empty();
90+
}
91+
92+
/**
93+
* Provides component hierarchy traversal.
94+
*
95+
* @param aContainer start node for the traversal.
96+
*/
97+
public static void traverse(Container aContainer, ApplyToComponent applicator) {
98+
for (final Component comp : aContainer.getComponents()) {
99+
if (comp instanceof JComponent) {
100+
applicator.apply((JComponent) comp);
101+
}
102+
if (comp instanceof Container) {
103+
traverse((Container) comp, applicator);
104+
}
105+
}
106+
}
107+
108+
public static Frame getRootFrame() {
109+
Frame[] frames = Frame.getFrames();
110+
if (frames.length == 0) {
111+
return null;
112+
}
113+
114+
return frames[0];
115+
}
116+
117+
@FunctionalInterface
118+
public interface ApplyToComponent {
119+
public void apply(JComponent component);
120+
}
119121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2024 Will Winder
3+
4+
This file is part of Universal Gcode Sender (UGS).
5+
6+
UGS is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
UGS is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with UGS. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.willwinder.ugs.nbp.designer.actions;
20+
21+
import com.willwinder.ugs.nbp.designer.entities.Anchor;
22+
import com.willwinder.ugs.nbp.designer.entities.Entity;
23+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionEvent;
24+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionListener;
25+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionManager;
26+
import com.willwinder.ugs.nbp.designer.logic.Controller;
27+
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
28+
import com.willwinder.ugs.nbp.lib.services.LocalizingService;
29+
import org.openide.awt.ActionID;
30+
import org.openide.awt.ActionRegistration;
31+
import org.openide.util.ImageUtilities;
32+
33+
import java.awt.event.ActionEvent;
34+
import java.awt.geom.Point2D;
35+
import java.util.List;
36+
37+
/**
38+
* An action for aligning objects
39+
*
40+
* @author Joacim Breiler
41+
*/
42+
@ActionID(
43+
category = LocalizingService.CATEGORY_DESIGNER,
44+
id = "AlignBottomAction")
45+
@ActionRegistration(
46+
iconBase = AlignBottomAction.SMALL_ICON_PATH,
47+
displayName = "Align bottom",
48+
lazy = false)
49+
public class AlignBottomAction extends AbstractDesignAction implements SelectionListener {
50+
public static final String SMALL_ICON_PATH = "img/alignbottom.svg";
51+
public static final String LARGE_ICON_PATH = "img/alignbottom24.svg";
52+
private final transient Controller controller;
53+
54+
public AlignBottomAction() {
55+
putValue("menuText", "Align bottom");
56+
putValue(NAME, "Align bottom");
57+
putValue("iconBase", SMALL_ICON_PATH);
58+
putValue(SHORT_DESCRIPTION, "Align the objects at the bottom of the first selected entity");
59+
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(SMALL_ICON_PATH, false));
60+
putValue(LARGE_ICON_KEY, ImageUtilities.loadImageIcon(LARGE_ICON_PATH, false));
61+
62+
this.controller = ControllerFactory.getController();
63+
SelectionManager selectionManager = controller.getSelectionManager();
64+
selectionManager.addSelectionListener(this);
65+
onSelectionEvent(new SelectionEvent());
66+
}
67+
68+
@Override
69+
public void actionPerformed(ActionEvent e) {
70+
List<Entity> selection = controller.getSelectionManager().getSelection();
71+
Entity entity = selection.get(0);
72+
Point2D destination = entity.getPosition(Anchor.BOTTOM_CENTER);
73+
74+
UndoActionList actionList = new UndoActionList();
75+
for (int i = 1; i < selection.size(); i++) {
76+
Point2D position = selection.get(i).getPosition(Anchor.BOTTOM_CENTER);
77+
actionList.add(new MoveAction(List.of(selection.get(i)), new Point2D.Double(0, destination.getY() - position.getY())));
78+
}
79+
80+
actionList.redo();
81+
controller.getUndoManager().addAction(actionList);
82+
}
83+
84+
@Override
85+
public void onSelectionEvent(SelectionEvent selectionEvent) {
86+
SelectionManager selectionManager = controller.getSelectionManager();
87+
setEnabled(selectionManager.getSelection().size() > 1);
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2024 Will Winder
3+
4+
This file is part of Universal Gcode Sender (UGS).
5+
6+
UGS is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
UGS is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with UGS. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.willwinder.ugs.nbp.designer.actions;
20+
21+
import com.willwinder.ugs.nbp.designer.entities.Anchor;
22+
import com.willwinder.ugs.nbp.designer.entities.Entity;
23+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionEvent;
24+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionListener;
25+
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionManager;
26+
import com.willwinder.ugs.nbp.designer.logic.Controller;
27+
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
28+
import com.willwinder.ugs.nbp.lib.services.LocalizingService;
29+
import org.openide.awt.ActionID;
30+
import org.openide.awt.ActionRegistration;
31+
import org.openide.util.ImageUtilities;
32+
33+
import java.awt.event.ActionEvent;
34+
import java.awt.geom.Point2D;
35+
import java.util.List;
36+
37+
/**
38+
* An action for aligning objects
39+
*
40+
* @author Joacim Breiler
41+
*/
42+
@ActionID(
43+
category = LocalizingService.CATEGORY_DESIGNER,
44+
id = "AlignCenterAction")
45+
@ActionRegistration(
46+
iconBase = AlignCenterAction.SMALL_ICON_PATH,
47+
displayName = "Align center",
48+
lazy = false)
49+
public class AlignCenterAction extends AbstractDesignAction implements SelectionListener {
50+
public static final String SMALL_ICON_PATH = "img/aligncenter.svg";
51+
public static final String LARGE_ICON_PATH = "img/aligncenter24.svg";
52+
private final transient Controller controller;
53+
54+
public AlignCenterAction() {
55+
putValue("menuText", "Align center");
56+
putValue(NAME, "Align center");
57+
putValue("iconBase", SMALL_ICON_PATH);
58+
putValue(SHORT_DESCRIPTION, "Align the objects in center of the first selected entity");
59+
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(SMALL_ICON_PATH, false));
60+
putValue(LARGE_ICON_KEY, ImageUtilities.loadImageIcon(LARGE_ICON_PATH, false));
61+
62+
this.controller = ControllerFactory.getController();
63+
SelectionManager selectionManager = controller.getSelectionManager();
64+
selectionManager.addSelectionListener(this);
65+
onSelectionEvent(new SelectionEvent());
66+
}
67+
68+
@Override
69+
public void actionPerformed(ActionEvent e) {
70+
List<Entity> selection = controller.getSelectionManager().getSelection();
71+
Entity entity = selection.get(0);
72+
Point2D destination = entity.getCenter();
73+
74+
UndoActionList actionList = new UndoActionList();
75+
for (int i = 1; i < selection.size(); i++) {
76+
Point2D position = selection.get(i).getPosition(Anchor.CENTER);
77+
actionList.add(new MoveAction(List.of(selection.get(i)), new Point2D.Double(destination.getX() - position.getX(), 0)));
78+
}
79+
80+
actionList.redo();
81+
controller.getUndoManager().addAction(actionList);
82+
}
83+
84+
@Override
85+
public void onSelectionEvent(SelectionEvent selectionEvent) {
86+
SelectionManager selectionManager = controller.getSelectionManager();
87+
setEnabled(selectionManager.getSelection().size() > 1);
88+
}
89+
}

0 commit comments

Comments
 (0)