feat: Optional dialog to crop a cover image when added.

This commit is contained in:
lantzelot-swe 2024-02-01 21:44:12 +01:00
parent 7dfa5ba30c
commit 5f5ebf5174
9 changed files with 526 additions and 10 deletions

View File

@ -35,6 +35,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.lantz.gui.screenshot.EditScreenshotDialog;
import se.lantz.gui.screenshot.cover.EditCoverDialog;
import se.lantz.model.InfoModel;
import se.lantz.model.MainViewModel;
import se.lantz.util.CustomUndoPlainDocument;
@ -748,6 +749,21 @@ public class ScreenshotsPanel extends JPanel
JOptionPane.ERROR_MESSAGE);
return null;
}
if (FileManager.isShowCropDialogForCover())
{
//Show a edit dialog
EditCoverDialog dialog = new EditCoverDialog(returnImage);
dialog.pack();
dialog.setLocationRelativeTo(MainWindow.getInstance());
if (dialog.showDialog())
{
returnImage = dialog.getEditedImage();
}
else
{
return null;
}
}
Image newImage = returnImage.getScaledInstance(130, 200, Image.SCALE_SMOOTH);
imageLabel.setIcon(new ImageIcon(newImage));
}

View File

@ -26,6 +26,7 @@ public class MiscPanel extends JPanel
private JCheckBox deleteOldInstallsCheckBox;
private JPanel screenshotsPanel;
private JCheckBox cropScreenCheckBox;
private JCheckBox cropCoverCheckBox;
public MiscPanel(PreferencesModel model)
{
@ -91,7 +92,7 @@ public class MiscPanel extends JPanel
startupPanel.setLayout(gbl_startupPanel);
GridBagConstraints gbc_managerVersionCheckBox = new GridBagConstraints();
gbc_managerVersionCheckBox.weightx = 1.0;
gbc_managerVersionCheckBox.insets = new Insets(5, 0, 5, 0);
gbc_managerVersionCheckBox.insets = new Insets(5, 0, 0, 0);
gbc_managerVersionCheckBox.anchor = GridBagConstraints.WEST;
gbc_managerVersionCheckBox.gridx = 0;
gbc_managerVersionCheckBox.gridy = 0;
@ -136,6 +137,7 @@ public class MiscPanel extends JPanel
getPcuaeVersionCheckBox().setSelected(model.isCheckPCUAEVersionAtStartup());
getDeleteOldInstallsCheckBox().setSelected(model.isDeleteOldInstallfilesAfterDownload());
getCropScreenCheckBox().setSelected(model.isCropScreenshots());
getCropCoverCheckBox().setSelected(model.isShowCropDialogForCover());
}
private SaveStatePrefPanel getSaveStatePrefPanel()
@ -196,18 +198,25 @@ public class MiscPanel extends JPanel
{
screenshotsPanel = new JPanel();
screenshotsPanel
.setBorder(new TitledBorder(null, "Screenshots", TitledBorder.LEADING, TitledBorder.TOP, null, null));
.setBorder(new TitledBorder(null, "Screenshots & Cover", TitledBorder.LEADING, TitledBorder.TOP, null, null));
GridBagLayout gbl_screenshotsPanel = new GridBagLayout();
gbl_screenshotsPanel.columnWidths = new int[] { 0, 0 };
gbl_screenshotsPanel.rowHeights = new int[] { 0, 0 };
gbl_screenshotsPanel.rowHeights = new int[] { 0, 0, 0 };
gbl_screenshotsPanel.columnWeights = new double[] { 0.0, Double.MIN_VALUE };
gbl_screenshotsPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
gbl_screenshotsPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
screenshotsPanel.setLayout(gbl_screenshotsPanel);
GridBagConstraints gbc_cropScreenCheckBox = new GridBagConstraints();
gbc_cropScreenCheckBox.insets = new Insets(5, 0, 5, 5);
gbc_cropScreenCheckBox.anchor = GridBagConstraints.NORTHWEST;
gbc_cropScreenCheckBox.insets = new Insets(5, 0, 0, 0);
gbc_cropScreenCheckBox.gridx = 0;
gbc_cropScreenCheckBox.gridy = 0;
screenshotsPanel.add(getCropScreenCheckBox(), gbc_cropScreenCheckBox);
GridBagConstraints gbc_cropCoverCheckBox = new GridBagConstraints();
gbc_cropCoverCheckBox.anchor = GridBagConstraints.NORTHWEST;
gbc_cropCoverCheckBox.insets = new Insets(0, 0, 5, 0);
gbc_cropCoverCheckBox.gridx = 0;
gbc_cropCoverCheckBox.gridy = 1;
screenshotsPanel.add(getCropCoverCheckBox(), gbc_cropCoverCheckBox);
}
return screenshotsPanel;
}
@ -222,4 +231,12 @@ public class MiscPanel extends JPanel
}
return cropScreenCheckBox;
}
private JCheckBox getCropCoverCheckBox() {
if (cropCoverCheckBox == null) {
cropCoverCheckBox = new JCheckBox("Show dialog to crop cover when added.");
cropCoverCheckBox.setVerticalTextPosition(SwingConstants.TOP);
cropCoverCheckBox.addItemListener((e) -> model.setShowCropDialogForCover(cropCoverCheckBox.isSelected()));
}
return cropCoverCheckBox;
}
}

View File

@ -16,7 +16,7 @@ public class PreferencesDialog extends BaseDialog
addContent(getPreferencesTabPanel());
getOkButton().setText("Save");
getOkButton().setPreferredSize(null);
this.setPreferredSize(new Dimension(368, 570));
this.setPreferredSize(new Dimension(368, 585));
this.setResizable(false);
}

View File

@ -1,10 +1,14 @@
package se.lantz.gui.screenshot;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
@ -197,7 +201,23 @@ public class EditScreenshotPanel extends JPanel
public void setImage(BufferedImage image)
{
this.image = image;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = (int)screenSize.getHeight();
int heightThreshold = screenHeight - 250;
//Handle very large images by scaling them down so that the dialog fits on the screen
if (image.getHeight() > heightThreshold)
{
Image scaledImage = image.getScaledInstance(-1, heightThreshold, Image.SCALE_SMOOTH);
BufferedImage newBufImage = new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = newBufImage.createGraphics();
bGr.drawImage(scaledImage, 0, 0, null);
bGr.dispose();
this.image = newBufImage;
}
else
{
this.image = image;
}
getLargeSizeCheckBox().setVisible(image.getWidth() > 447 && image.getHeight() > 279);
x = (image.getWidth() - width) / 2;
y = ((image.getHeight() - height) / 2) - 1;
@ -255,8 +275,22 @@ public class EditScreenshotPanel extends JPanel
public BufferedImage getCroppedImage()
{
int xToUse = x < 0 ? 0 : x;
int yToUse = y < 0 ? 0 : y;
int widthToUse = width;
int heightToUse = height;
if ((xToUse + widthToUse) > image.getWidth())
{
widthToUse = image.getWidth() - xToUse;
}
if ((yToUse + heightToUse) > image.getHeight())
{
heightToUse = image.getHeight() - yToUse;
}
BufferedImage newImage = image
.getSubimage(x, y, width, height);
.getSubimage(xToUse, yToUse, widthToUse, heightToUse);
BufferedImage copyOfImage =
new BufferedImage(newImage.getWidth(), newImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = copyOfImage.createGraphics();

View File

@ -26,7 +26,7 @@ public class TypomaticButton extends JButton implements MouseListener
addMouseListener(this);
}
TypomaticButton(String text)
public TypomaticButton(String text)
{
super(text);
setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));

View File

@ -0,0 +1,46 @@
package se.lantz.gui.screenshot.cover;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import se.lantz.gui.BaseDialog;
import se.lantz.gui.MainWindow;
public class EditCoverDialog extends BaseDialog
{
private EditCoverPanel editScreenPanel;
public EditCoverDialog(BufferedImage originalScreen)
{
super(MainWindow.getInstance());
setTitle("Edit screenshot");
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(getEditScreenshotPanel(), BorderLayout.CENTER);
addContent(content);
getEditScreenshotPanel().setImage(originalScreen);
this.setResizable(false);
}
private EditCoverPanel getEditScreenshotPanel()
{
if (editScreenPanel == null)
{
editScreenPanel = new EditCoverPanel();
}
return editScreenPanel;
}
@Override
public boolean showDialog()
{
return super.showDialog();
}
public BufferedImage getEditedImage()
{
return getEditScreenshotPanel().getCroppedImage();
}
}

View File

@ -0,0 +1,374 @@
package se.lantz.gui.screenshot.cover;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import se.lantz.gui.screenshot.TypomaticButton;
public class EditCoverPanel extends JPanel
{
private JLabel imageLabel;
private JPanel buttonPanel;
private TypomaticButton leftButton;
private TypomaticButton righButton;
private BufferedImage image;
private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
private TypomaticButton upButton;
private TypomaticButton downButton;
private JLabel infoLabel;
public EditCoverPanel()
{
GridBagLayout gridBagLayout = new GridBagLayout();
setLayout(gridBagLayout);
GridBagConstraints gbc_infoLabel = new GridBagConstraints();
gbc_infoLabel.ipady = 10;
gbc_infoLabel.fill = GridBagConstraints.BOTH;
gbc_infoLabel.insets = new Insets(10, 0, 0, 0);
gbc_infoLabel.gridx = 0;
gbc_infoLabel.gridy = 0;
add(getInfoLabel(), gbc_infoLabel);
GridBagConstraints gbc_imageLabel = new GridBagConstraints();
gbc_imageLabel.weighty = 1.0;
gbc_imageLabel.insets = new Insets(0, 10, 10, 10);
gbc_imageLabel.weightx = 1.0;
gbc_imageLabel.gridx = 0;
gbc_imageLabel.gridy = 1;
add(getImageLabel(), gbc_imageLabel);
GridBagConstraints gbc_buttonPanel = new GridBagConstraints();
gbc_buttonPanel.anchor = GridBagConstraints.NORTH;
gbc_buttonPanel.gridx = 0;
gbc_buttonPanel.gridy = 3;
add(getButtonPanel(), gbc_buttonPanel);
InputMap inputMap = this.getInputMap(WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
getActionMap().put("left", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
performLeftAction();
}
});
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
getActionMap().put("right", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
performRightAction();
}
});
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
getActionMap().put("up", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
performUpAction();
}
});
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
getActionMap().put("down", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
performDownAction();
}
});
}
private void performLeftAction()
{
if (x > 0)
{
x = x - 1;
updateLabelIcon();
}
}
private void performRightAction()
{
if (x < (image.getWidth() - width - 1))
{
x = x + 1;
updateLabelIcon();
}
}
private void performUpAction()
{
if (y > 0)
{
y = y - 1;
updateLabelIcon();
}
}
private void performDownAction()
{
if (y < (image.getHeight() - height - 1))
{
y = y + 1;
updateLabelIcon();
}
}
private JLabel getImageLabel()
{
if (imageLabel == null)
{
imageLabel = new JLabel("");
imageLabel.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
System.out.println("Mouse pressed!" + e.getPoint());
x = e.getX();
y = e.getY();
width = 0;
height = 0;
updateLabelIcon();
}
});
imageLabel.addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseDragged(MouseEvent e)
{
System.out.println("Mouse dragged!" + e.getPoint());
mouseDrag(e.getX(), e.getY());
}
});
}
return imageLabel;
}
private JPanel getButtonPanel()
{
if (buttonPanel == null)
{
buttonPanel = new JPanel();
buttonPanel.add(getLeftButton());
buttonPanel.add(getRighButton());
buttonPanel.add(getUpButton());
buttonPanel.add(getDownButton());
}
return buttonPanel;
}
private TypomaticButton getLeftButton()
{
if (leftButton == null)
{
leftButton = new TypomaticButton("");
leftButton.setIcon(new ImageIcon(this.getClass().getResource("/se/lantz/arrow-plain-left.png")));
leftButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
performLeftAction();
}
});
}
return leftButton;
}
private TypomaticButton getRighButton()
{
if (righButton == null)
{
righButton = new TypomaticButton("");
righButton.setIcon(new ImageIcon(this.getClass().getResource("/se/lantz/arrow-plain-right.png")));
righButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
performRightAction();
}
});
}
return righButton;
}
public void setImage(BufferedImage image)
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = (int) screenSize.getHeight();
int heightThreshold = screenHeight - 250;
//Handle very large images by scaling them down so that the dialog fits on the screen
if (image.getHeight() > heightThreshold)
{
Image scaledImage = image.getScaledInstance(-1, heightThreshold, Image.SCALE_SMOOTH);
BufferedImage newBufImage =
new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = newBufImage.createGraphics();
bGr.drawImage(scaledImage, 0, 0, null);
bGr.dispose();
this.image = newBufImage;
}
else
{
this.image = image;
}
updateLabelIcon();
}
private void updateLabelIcon()
{
BufferedImage copyOfImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = copyOfImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.setColor(Color.red);
g.setStroke(new BasicStroke(2));
g.drawRect(x, y, width, height);
getImageLabel().setIcon(new ImageIcon(copyOfImage));
}
public BufferedImage getImage()
{
return this.image;
}
private TypomaticButton getUpButton()
{
if (upButton == null)
{
upButton = new TypomaticButton("");
upButton.setIcon(new ImageIcon(this.getClass().getResource("/se/lantz/arrow-plain-up.png")));
upButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
performUpAction();
}
});
}
return upButton;
}
private TypomaticButton getDownButton()
{
if (downButton == null)
{
downButton = new TypomaticButton("");
downButton.setIcon(new ImageIcon(this.getClass().getResource("/se/lantz/arrow-plain-down.png")));
downButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
performDownAction();
}
});
}
return downButton;
}
public BufferedImage getCroppedImage()
{
if (width < 5 || height < 5)
{
return image;
}
BufferedImage newImage = image.getSubimage(x, y, width, height);
BufferedImage copyOfImage =
new BufferedImage(newImage.getWidth(), newImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = copyOfImage.createGraphics();
g.drawImage(newImage, 0, 0, null);
return newImage;
}
private JLabel getInfoLabel()
{
if (infoLabel == null)
{
infoLabel =
new JLabel("<html>Left-click and drag to mark the area to crop.<br>Move the rectangle with the arrow buttons.</html>");
infoLabel.setVerticalAlignment(SwingConstants.TOP);
infoLabel.setHorizontalAlignment(SwingConstants.CENTER);
infoLabel.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
//The image might not be at position 0 if the image is small, compensate for that.
x = e.getX() - imageLabel.getLocation().x;
if (x < 0)
{
x = 0;
}
y = 0;
width = 0;
height = 0;
updateLabelIcon();
}
});
infoLabel.addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseDragged(MouseEvent e)
{
System.out.println("Mouse dragged on label!" + e.getPoint());
int ypos = e.getY();
if (e.getY() > (infoLabel.getHeight()))
{
ypos = e.getY() - infoLabel.getHeight();
mouseDrag(e.getX(), ypos);
}
}
});
}
return infoLabel;
}
private void mouseDrag(int xPos, int yPos)
{
width = xPos - x;
height = yPos - y;
if (x + width > image.getWidth())
{
width = image.getWidth() - x;
}
if (y + height > image.getHeight())
{
height = image.getHeight() - y;
}
updateLabelIcon();
}
}

View File

@ -15,6 +15,7 @@ public class PreferencesModel extends AbstractModel implements CommonInfoModel
public static final String MANGER_VERSION_CHECK = "checkForManagerVersion";
public static final String DELETE_OLD_INSTALL_FILES = "deleteOldInstallFiles";
public static final String CROP_SCREENSHOTS = "cropScreenshotsWhenAdded";
public static final String SHOW_CROP_DIALOG_FOR_COVER = "showCropDialogForCover";
public static final String GENRE = "infoSlotGenre";
public static final String AUTHOR = "infoSlotAuthor";
public static final String YEAR = "infoSlotYear";
@ -43,6 +44,7 @@ public class PreferencesModel extends AbstractModel implements CommonInfoModel
private boolean checkManagerVersionAtStartup = true;
private boolean deleteOldInstallfilesAfterDownload = false;
private boolean cropScreenshots = false;
private boolean cropDialogForCover = false;
private String description =
"For more Info on PCUAE go to https://github.com/CommodoreOS/PCUAE. Main keys: CTRL + F1 for Carousel Gamelist Changer, CTRL + F3 for Carousel Version Changer, CTRL + F5 for Mode Changer (Amiga, Atari, linux etc), CTRL + F7 for PCUAE Options Menu.";
@ -92,7 +94,7 @@ public class PreferencesModel extends AbstractModel implements CommonInfoModel
setDeleteOldInstallfilesAfterDownload(Boolean
.parseBoolean(configuredProperties.getProperty(DELETE_OLD_INSTALL_FILES, "false")));
setCropScreenshots(Boolean.parseBoolean(configuredProperties.getProperty(CROP_SCREENSHOTS, "false")));
setShowCropDialogForCover(Boolean.parseBoolean(configuredProperties.getProperty(SHOW_CROP_DIALOG_FOR_COVER, "false")));
setFav1Alias(configuredProperties.getProperty(FAVORITES_1_ALIAS, fav1Alias));
setFav2Alias(configuredProperties.getProperty(FAVORITES_2_ALIAS, fav2Alias));
setFav3Alias(configuredProperties.getProperty(FAVORITES_3_ALIAS, fav3Alias));
@ -514,6 +516,21 @@ public class PreferencesModel extends AbstractModel implements CommonInfoModel
notifyChange();
}
}
public void setShowCropDialogForCover(boolean cropDialogForCover)
{
boolean old = isShowCropDialogForCover();
this.cropDialogForCover = cropDialogForCover;
if ((Boolean.compare(old, cropDialogForCover) != 0))
{
notifyChange();
}
}
public boolean isShowCropDialogForCover()
{
return cropDialogForCover;
}
public void savePreferences()
{
@ -535,6 +552,7 @@ public class PreferencesModel extends AbstractModel implements CommonInfoModel
configuredProperties.put(SAVED_STATES_CAROUSEL, savedStatesCarouselVersion);
configuredProperties.put(DELETE_OLD_INSTALL_FILES, Boolean.toString(deleteOldInstallfilesAfterDownload));
configuredProperties.put(CROP_SCREENSHOTS, Boolean.toString(cropScreenshots));
configuredProperties.put(SHOW_CROP_DIALOG_FOR_COVER, Boolean.toString(cropDialogForCover));
configuredProperties.put(FAVORITES_1_ALIAS, fav1Alias);
configuredProperties.put(FAVORITES_2_ALIAS, fav2Alias);
configuredProperties.put(FAVORITES_3_ALIAS, fav3Alias);

View File

@ -85,6 +85,7 @@ public class FileManager
private static String currentSavedStatesCarouselVersion = "";
private static String deleteInstallFiles = "";
private static String cropScreenshots = "";
private static String showCropDialogForCover = "";
private MainViewModel model;
private InfoModel infoModel;
@ -1275,6 +1276,7 @@ public class FileManager
currentSavedStatesCarouselVersion = "";
deleteInstallFiles = "";
cropScreenshots = "";
showCropDialogForCover = "";
try (OutputStream output = new FileOutputStream("./pcu.properties"))
{
// save properties to project root folder
@ -1367,6 +1369,15 @@ public class FileManager
}
return Boolean.parseBoolean(cropScreenshots);
}
public static boolean isShowCropDialogForCover()
{
if (showCropDialogForCover.isEmpty())
{
showCropDialogForCover = FileManager.getConfiguredProperties().getProperty(PreferencesModel.SHOW_CROP_DIALOG_FOR_COVER, "false");
}
return Boolean.parseBoolean(showCropDialogForCover);
}
public static void backupDb(String targetFolderName)
{