Skip to content

Commit

Permalink
Added actions for aligning entities in the designer (#2577)
Browse files Browse the repository at this point in the history
  • Loading branch information
breiler authored Jul 27, 2024
1 parent 57e1160 commit b93cf32
Show file tree
Hide file tree
Showing 59 changed files with 2,438 additions and 140 deletions.
200 changes: 101 additions & 99 deletions ugs-core/src/com/willwinder/universalgcodesender/utils/SwingHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,117 +3,119 @@
import com.willwinder.universalgcodesender.i18n.Localization;
import com.willwinder.universalgcodesender.model.UnitUtils;
import com.willwinder.universalgcodesender.uielements.components.GcodeFileTypeFilter;
import java.awt.Component;
import java.awt.Container;
import java.io.File;
import java.util.Optional;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SpinnerNumberModel;
import javax.swing.filechooser.FileFilter;
import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.io.File;
import java.util.Optional;

/**
*
* @author wwinder
*/
public class SwingHelpers {
private static final String[] UNIT_OPTIONS = {
Localization.getString("mainWindow.swing.mmRadioButton"),
Localization.getString("mainWindow.swing.inchRadioButton")
};

public static String[] getUnitOptions() {
return UNIT_OPTIONS;
}

public static UnitUtils.Units selectedUnit(int idx) {
return idx == 0 ? UnitUtils.Units.MM : UnitUtils.Units.INCH;
}

public static int unitIdx(UnitUtils.Units units) {
if (units == UnitUtils.Units.INCH) {
return 1;
} else {
return 0;
private static final String[] UNIT_OPTIONS = {
Localization.getString("mainWindow.swing.mmRadioButton"),
Localization.getString("mainWindow.swing.inchRadioButton")
};

public static String[] getUnitOptions() {
return UNIT_OPTIONS;
}
}

// deal with casting the spinner model to a double.
public static double getDouble(SpinnerNumberModel model) {
return (double) model.getValue();
}

// deal with casting the spinner model to a double.
public static int getInt(SpinnerNumberModel model) {
return (int) model.getValue();
}

public static Optional<File> createFile(String sourceDir) {
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
int returnVal = fileChooser.showSaveDialog(new JFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
return Optional.ofNullable(fileChooser.getSelectedFile());
} else {
return Optional.empty();
}
}

public static Optional<File> openFile(String sourceDir) {
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
int returnVal = fileChooser.showOpenDialog(new JFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
return Optional.ofNullable(fileChooser.getSelectedFile());
} else {
return Optional.empty();
}
}

public static Optional<File> openDirectory(String title, File defaultDirectory) {
JFileChooser chooser = new JFileChooser();
if(defaultDirectory != null && defaultDirectory.isDirectory()) {
chooser.setCurrentDirectory(defaultDirectory);

public static UnitUtils.Units selectedUnit(int idx) {
return idx == 0 ? UnitUtils.Units.MM : UnitUtils.Units.INCH;
}
chooser.setDialogTitle(title);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory();
}

@Override
public String getDescription() {
return "Directories";
}
});

if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
return Optional.of(chooser.getSelectedFile());

// deal with casting the spinner model to a double.
public static double getDouble(SpinnerNumberModel model) {
return (double) model.getValue();
}

return Optional.empty();
}

@FunctionalInterface
public interface ApplyToComponent {
public void apply(JComponent component);
}

/**
* Provides component hierarchy traversal.
*
* @param aContainer start node for the traversal.
*/
public static void traverse(Container aContainer, ApplyToComponent applicator) {
for (final Component comp : aContainer.getComponents()) {
if (comp instanceof JComponent) {
applicator.apply((JComponent) comp);
}
if (comp instanceof Container) {
traverse((Container) comp, applicator);
}
}
}
// deal with casting the spinner model to a double.
public static int getInt(SpinnerNumberModel model) {
return (int) model.getValue();
}

public static Optional<File> createFile(String sourceDir) {
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
int returnVal = fileChooser.showSaveDialog(new JFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
return Optional.ofNullable(fileChooser.getSelectedFile());
} else {
return Optional.empty();
}
}

public static Optional<File> openFile(String sourceDir) {
JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser(sourceDir);
int returnVal = fileChooser.showOpenDialog(new JFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
return Optional.ofNullable(fileChooser.getSelectedFile());
} else {
return Optional.empty();
}
}

public static Optional<File> openDirectory(String title, File defaultDirectory) {
JFileChooser chooser = new JFileChooser();
if (defaultDirectory != null && defaultDirectory.isDirectory()) {
chooser.setCurrentDirectory(defaultDirectory);
}
chooser.setDialogTitle(title);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory();
}

@Override
public String getDescription() {
return "Directories";
}
});

if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
return Optional.of(chooser.getSelectedFile());
}

return Optional.empty();
}

/**
* Provides component hierarchy traversal.
*
* @param aContainer start node for the traversal.
*/
public static void traverse(Container aContainer, ApplyToComponent applicator) {
for (final Component comp : aContainer.getComponents()) {
if (comp instanceof JComponent) {
applicator.apply((JComponent) comp);
}
if (comp instanceof Container) {
traverse((Container) comp, applicator);
}
}
}

public static Frame getRootFrame() {
Frame[] frames = Frame.getFrames();
if (frames.length == 0) {
return null;
}

return frames[0];
}

@FunctionalInterface
public interface ApplyToComponent {
public void apply(JComponent component);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.ugs.nbp.designer.actions;

import com.willwinder.ugs.nbp.designer.entities.Anchor;
import com.willwinder.ugs.nbp.designer.entities.Entity;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionEvent;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionListener;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionManager;
import com.willwinder.ugs.nbp.designer.logic.Controller;
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
import com.willwinder.ugs.nbp.lib.services.LocalizingService;
import org.openide.awt.ActionID;
import org.openide.awt.ActionRegistration;
import org.openide.util.ImageUtilities;

import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.util.List;

/**
* An action for aligning objects
*
* @author Joacim Breiler
*/
@ActionID(
category = LocalizingService.CATEGORY_DESIGNER,
id = "AlignBottomAction")
@ActionRegistration(
iconBase = AlignBottomAction.SMALL_ICON_PATH,
displayName = "Align bottom",
lazy = false)
public class AlignBottomAction extends AbstractDesignAction implements SelectionListener {
public static final String SMALL_ICON_PATH = "img/alignbottom.svg";
public static final String LARGE_ICON_PATH = "img/alignbottom24.svg";
private final transient Controller controller;

public AlignBottomAction() {
putValue("menuText", "Align bottom");
putValue(NAME, "Align bottom");
putValue("iconBase", SMALL_ICON_PATH);
putValue(SHORT_DESCRIPTION, "Align the objects at the bottom of the first selected entity");
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(SMALL_ICON_PATH, false));
putValue(LARGE_ICON_KEY, ImageUtilities.loadImageIcon(LARGE_ICON_PATH, false));

this.controller = ControllerFactory.getController();
SelectionManager selectionManager = controller.getSelectionManager();
selectionManager.addSelectionListener(this);
onSelectionEvent(new SelectionEvent());
}

@Override
public void actionPerformed(ActionEvent e) {
List<Entity> selection = controller.getSelectionManager().getSelection();
Entity entity = selection.get(0);
Point2D destination = entity.getPosition(Anchor.BOTTOM_CENTER);

UndoActionList actionList = new UndoActionList();
for (int i = 1; i < selection.size(); i++) {
Point2D position = selection.get(i).getPosition(Anchor.BOTTOM_CENTER);
actionList.add(new MoveAction(List.of(selection.get(i)), new Point2D.Double(0, destination.getY() - position.getY())));
}

actionList.redo();
controller.getUndoManager().addAction(actionList);
}

@Override
public void onSelectionEvent(SelectionEvent selectionEvent) {
SelectionManager selectionManager = controller.getSelectionManager();
setEnabled(selectionManager.getSelection().size() > 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.ugs.nbp.designer.actions;

import com.willwinder.ugs.nbp.designer.entities.Anchor;
import com.willwinder.ugs.nbp.designer.entities.Entity;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionEvent;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionListener;
import com.willwinder.ugs.nbp.designer.entities.selection.SelectionManager;
import com.willwinder.ugs.nbp.designer.logic.Controller;
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
import com.willwinder.ugs.nbp.lib.services.LocalizingService;
import org.openide.awt.ActionID;
import org.openide.awt.ActionRegistration;
import org.openide.util.ImageUtilities;

import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.util.List;

/**
* An action for aligning objects
*
* @author Joacim Breiler
*/
@ActionID(
category = LocalizingService.CATEGORY_DESIGNER,
id = "AlignCenterAction")
@ActionRegistration(
iconBase = AlignCenterAction.SMALL_ICON_PATH,
displayName = "Align center",
lazy = false)
public class AlignCenterAction extends AbstractDesignAction implements SelectionListener {
public static final String SMALL_ICON_PATH = "img/aligncenter.svg";
public static final String LARGE_ICON_PATH = "img/aligncenter24.svg";
private final transient Controller controller;

public AlignCenterAction() {
putValue("menuText", "Align center");
putValue(NAME, "Align center");
putValue("iconBase", SMALL_ICON_PATH);
putValue(SHORT_DESCRIPTION, "Align the objects in center of the first selected entity");
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(SMALL_ICON_PATH, false));
putValue(LARGE_ICON_KEY, ImageUtilities.loadImageIcon(LARGE_ICON_PATH, false));

this.controller = ControllerFactory.getController();
SelectionManager selectionManager = controller.getSelectionManager();
selectionManager.addSelectionListener(this);
onSelectionEvent(new SelectionEvent());
}

@Override
public void actionPerformed(ActionEvent e) {
List<Entity> selection = controller.getSelectionManager().getSelection();
Entity entity = selection.get(0);
Point2D destination = entity.getCenter();

UndoActionList actionList = new UndoActionList();
for (int i = 1; i < selection.size(); i++) {
Point2D position = selection.get(i).getPosition(Anchor.CENTER);
actionList.add(new MoveAction(List.of(selection.get(i)), new Point2D.Double(destination.getX() - position.getX(), 0)));
}

actionList.redo();
controller.getUndoManager().addAction(actionList);
}

@Override
public void onSelectionEvent(SelectionEvent selectionEvent) {
SelectionManager selectionManager = controller.getSelectionManager();
setEnabled(selectionManager.getSelection().size() > 1);
}
}
Loading

0 comments on commit b93cf32

Please sign in to comment.