From bf5a54d500d7c280784ae04746cc9d59f653314e Mon Sep 17 00:00:00 2001 From: Josh Ashton Date: Sun, 24 Mar 2024 17:00:55 -0600 Subject: [PATCH] added backspace support and basic error checking in settings and gui. 2 major bugs: empty cells always start in note mode, and the sizing is not constant. --- src/gui/Board.java | 57 +++++++++----- src/gui/CellGUI.java | 141 +++++++++++++++++++++++++++++----- src/gui/backend/Cell.java | 12 +++ src/gui/backend/Settings.java | 57 +++++++++++++- src/gui/backend/Theme.java | 62 ++++++++++++++- 5 files changed, 286 insertions(+), 43 deletions(-) diff --git a/src/gui/Board.java b/src/gui/Board.java index 39acf2b..ea636c8 100644 --- a/src/gui/Board.java +++ b/src/gui/Board.java @@ -1,5 +1,6 @@ package gui; +import java.awt.Dimension; import java.awt.GridLayout; import java.awt.LayoutManager; import java.awt.event.KeyAdapter; @@ -29,6 +30,7 @@ import gui.backend.SudokuChecker; public class Board extends JPanel { private Settings s; private Cell[][] grid; + private Cell[][] solvedGrid; private CellGUI[][] gridGUI; private SudokuChecker sc; private CellGUI selected; @@ -43,6 +45,8 @@ public class Board extends JPanel { this.s = s; this.grid = grid; this.sc = sc; + solvedGrid = new SudokuChecker(Cell.copyGrid(grid)).getSolution(); + style(); createBoard(); } @@ -50,30 +54,42 @@ public class Board extends JPanel { private KeyListener createKeyListener() { return new KeyListener() { @Override - public void keyTyped(KeyEvent e) { - char key = e.getKeyChar(); - if(!Character.isDigit(key)) return; - if(selected.cell.isInitValue()) return; - - if(e.getKeyCode() == e.VK_BACK_SPACE) { - if(selected.isInNotesMode()) { - System.out.println("Remove all notes."); - return; - } else { - selected.setValue(0); - return; - } - } - - if(!selected.isInNotesMode()) - selected.setValue(Character.getNumericValue(key)); - } + public void keyTyped(KeyEvent e) {} @Override public void keyPressed(KeyEvent e) {} @Override - public void keyReleased(KeyEvent e) {} + public void keyReleased(KeyEvent e) { + char key = e.getKeyChar(); + if(selected.cell.isInitValue()) return; + + // Backspace key code is 0 + if(e.getKeyCode() == e.VK_BACK_SPACE) { + selected.removeValue(); + selected.repaint(); + selected.revalidate(); + return; + } + + if(!Character.isDigit(key)) return; + + if(!selected.isInNotesMode()) { + if(s.getAutoCheckValues()) { + int row = selected.cell.getRow(); + int col = selected.cell.getCol(); + int expected = solvedGrid[row][col].getValue(); + selected.setValue(Character.getNumericValue(key), expected); + } else { + selected.setValue(Character.getNumericValue(key)); + } + } else { + selected.addPossibleValue(Character.getNumericValue(key)); + } + + selected.repaint(); + selected.revalidate(); + } }; } @@ -171,7 +187,8 @@ public class Board extends JPanel { CellGUI cell = new CellGUI( grid[i][j], s.getCellGUIStartMode(), - s.getTheme() + s.getTheme(), + s.getCellDimensions() ); cell.addMouseListener(new MouseListener() { @Override diff --git a/src/gui/CellGUI.java b/src/gui/CellGUI.java index 4a7c418..953ce07 100644 --- a/src/gui/CellGUI.java +++ b/src/gui/CellGUI.java @@ -1,6 +1,7 @@ package gui; import java.awt.BorderLayout; +import java.awt.Dimension; import java.awt.GridLayout; import javax.swing.JLabel; @@ -19,9 +20,11 @@ class CellGUI extends JPanel { private JPanel internalPanel = new JPanel(new BorderLayout()); private GridLayout noteLayout = new GridLayout(3, 3); private GridLayout valueLayout = new GridLayout(0, 1); + private Dimension size; private JLabel valueLabel; private Note[] notesLabels = new Note[9]; private Theme theme; + private boolean incorrect; // Data fields Cell cell; @@ -36,7 +39,7 @@ class CellGUI extends JPanel { * @param cell * @param startInNotesOrValueMode */ - public CellGUI(Cell cell, boolean noteMode, Theme theme) { + public CellGUI(Cell cell, boolean noteMode, Theme theme, Dimension size) { super(); this.cell = cell; this.noteMode = !noteMode; // Flip for use with setNoteMode() @@ -45,7 +48,9 @@ class CellGUI extends JPanel { setFocusable(true); setLayout(valueLayout); valueLabel = new JLabel("", SwingConstants.CENTER); - style(); + this.size = size; + setSize(this.size); + defaultStyle(); // Populate the cell with the appropriate value or notes. if(cell.getValue() == 0) { @@ -59,6 +64,25 @@ class CellGUI extends JPanel { add(internalPanel); } + /** + * Get the size of the cell. + */ + public Dimension getSize() { + return size; + } + + /** + * Set the size of the cell. + */ + public void setSize(Dimension size) { + this.size = size; + super.setSize(size); + internalPanel.setSize(size); + valueLabel.setSize(size); + for(int i = 0; i < 9; i++) + if(notesLabels[i] != null) notesLabels[i].setSize(size); + } + /** * Get the value of the underlying Cell object. * @@ -81,6 +105,45 @@ class CellGUI extends JPanel { internalPanel.revalidate(); } + /** + * Set the value of the underlying Cell object. + * + * This implementation allows for incorrect values to be highlighted. + * + * @param value + */ + public void setValue(int value, int expected) { + if(!selected) return; + + cell.setValue(value); + valueLabel.setText(Integer.toString(cell.getValue())); + + if(value != expected) { + incorrect = true; + errorStyle(); + } + + internalPanel.repaint(); + internalPanel.revalidate(); + } + + /** + * Remove the value of the underlying Cell object. + */ + public void removeValue() { + if(!selected) return; + + cell.setValue(0); + if(!cell.isInitValue()) + valueLabel.setText(""); + + incorrect = false; + highlightedStyle(); + + internalPanel.repaint(); + internalPanel.revalidate(); + } + /** * Set the possible values of the underlying Cell object. * @@ -96,8 +159,13 @@ class CellGUI extends JPanel { * @param value */ public void addPossibleValue(int value) { + if(!selected) return; + cell.addPossibleValue(value); - setNoteMode(); + internalPanel.removeAll(); + internalPanel.setLayout(noteLayout); + generateNotes(); + highlightedStyle(); } /** @@ -155,24 +223,17 @@ class CellGUI extends JPanel { public void select() { selected = !selected; - - if(!selected) { - style(); - } else { - setBackground(theme.getSecondaryBackground()); - setForeground(theme.getSecondaryText()); - internalPanel.setBackground(theme.getSecondaryBackground()); - internalPanel.setForeground(theme.getSecondaryText()); - valueLabel.setForeground(theme.getSecondaryText()); - setBorder(new LineBorder(theme.getSecondaryBorder(), 2)); + if(incorrect) { + errorStyle(); + repaint(); + revalidate(); + return; + } - for(int i = 0; i < 9; i++) { - if(notesLabels[i] != null) { - notesLabels[i].setBackground(theme.getSecondaryBackground()); - notesLabels[i].setForeground(theme.getSecondaryText()); - } - } - } + if(!selected) + defaultStyle(); + + else highlightedStyle(); repaint(); revalidate(); @@ -181,7 +242,7 @@ class CellGUI extends JPanel { /** * Style the cell with the appropriate colors from the theme. */ - private void style() { + private void defaultStyle() { setBackground(theme.getPrimaryBackground()); setForeground(theme.getPrimaryText()); internalPanel.setBackground(theme.getPrimaryBackground()); @@ -197,6 +258,44 @@ class CellGUI extends JPanel { } } + /** + * Style the cell with the appropriate colors from the theme. + */ + private void highlightedStyle() { + setBackground(theme.getSecondaryBackground()); + setForeground(theme.getSecondaryText()); + internalPanel.setBackground(theme.getSecondaryBackground()); + internalPanel.setForeground(theme.getSecondaryText()); + valueLabel.setForeground(theme.getSecondaryText()); + setBorder(new LineBorder(theme.getSecondaryBorder(), 2)); + + for(int i = 0; i < 9; i++) { + if(notesLabels[i] != null) { + notesLabels[i].setBackground(theme.getSecondaryBackground()); + notesLabels[i].setForeground(theme.getSecondaryText()); + } + } + } + + /** + * Style the cell with the appropriate colors from the theme. + */ + private void errorStyle() { + setBackground(theme.getErrorBackground()); + setForeground(theme.getErrorText()); + internalPanel.setBackground(theme.getErrorBackground()); + internalPanel.setForeground(theme.getErrorText()); + valueLabel.setForeground(theme.getErrorText()); + setBorder(new LineBorder(theme.getErrorBorder(), 2)); + + for(int i = 0; i < 9; i++) { + if(notesLabels[i] != null) { + notesLabels[i].setBackground(theme.getErrorBackground()); + notesLabels[i].setForeground(theme.getErrorText()); + } + } + } + /** * Create the GUI components for the cell. */ diff --git a/src/gui/backend/Cell.java b/src/gui/backend/Cell.java index 6ba91d6..c77cabc 100644 --- a/src/gui/backend/Cell.java +++ b/src/gui/backend/Cell.java @@ -126,6 +126,7 @@ public class Cell { if(initValue) return; if(possibleValues.contains(value)) { + removePossibleValue(value); return; } else if(value < 1 || value > 9) { return; @@ -181,6 +182,17 @@ public class Cell { return initValue; } + public static Cell[][] copyGrid(Cell[][] grid) { + Cell[][] newGrid = new Cell[9][9]; + for(int row = 0; row < 9; row++) { + for(int col = 0; col < 9; col++) { + newGrid[row][col] = new Cell(row, col, grid[row][col].getValue()); + } + } + + return newGrid; + } + /** * Given the cell's row and column, set the box number for the cell. */ diff --git a/src/gui/backend/Settings.java b/src/gui/backend/Settings.java index 2d2994b..c2c3469 100644 --- a/src/gui/backend/Settings.java +++ b/src/gui/backend/Settings.java @@ -41,6 +41,9 @@ public class Settings { // The default dimension of the application window. private Dimension dimension; + // The default dimension of the cells in the application window. + private Dimension cellDimension; + // Resizable setting private boolean resizable; @@ -56,6 +59,9 @@ public class Settings { // Auto-fill notes private boolean autoFillNotes; + // Auto check values + private boolean autoCheckValues; + /** * Create a new Settings object, initializing the default settings * or reading in the settings from settings file if it exists. @@ -310,6 +316,53 @@ public class Settings { updateSettingsFile(); } + /** + * The Auto-check values setting is a configurable setting, where the user + * can specify whether values should be automatically checked when placed + * in a cell. + * + * By default, the auto-check values setting is set to false. + * + * @return + */ + public boolean getAutoCheckValues() { + return autoCheckValues; + } + + /** + * Set the auto-check values setting and write it to the settings file. + * + * @param autoCheckValues + */ + public void setAutoCheckValues(boolean autoCheckValues) { + this.autoCheckValues = autoCheckValues; + updateSettingsFile(); + } + + /** + * The default dimensions of the cells in the application window. + * + * The default dimensions are set to 50x50 pixels. + * + * @return + */ + public Dimension getCellDimensions() { + return cellDimension; + } + + private void setCellDimensions() { + int height; + int width; + + if(dimension.height < 500) height = 50; + else height = dimension.height / 10; + + if(dimension.width < 500) width = 50; + else width = dimension.width / 10; + + cellDimension = new Dimension(width, height); + } + /** * Open and write the settings to the settings file. * @@ -346,7 +399,9 @@ public class Settings { cellGUIStartMode = false; defaultOpenState = 0; theme = new Theme(new File(appDirectory + "default.theme")); - autoFillNotes = true; + autoFillNotes = false; + autoCheckValues = true; + setCellDimensions(); updateSettingsFile(); } diff --git a/src/gui/backend/Theme.java b/src/gui/backend/Theme.java index 3005331..4ea4e29 100644 --- a/src/gui/backend/Theme.java +++ b/src/gui/backend/Theme.java @@ -32,6 +32,8 @@ import java.io.File; * It provides colors for various GUI elements. */ public class Theme { + private File theme; + private Color primaryBackground; private Color secondaryBackground; private Color primaryText; @@ -46,6 +48,9 @@ public class Theme { private Color secondaryButtonHighlight; private Color primaryButtonBorder; private Color secondaryButtonBorder; + private Color errorBackground; + private Color errorText; + private Color errorBorder; /** * Constructs a new Theme object with the given theme file. @@ -77,6 +82,9 @@ public class Theme { secondaryText = Color.decode("#4A245E"); primaryBorder = Color.decode("#A76BCA"); secondaryBorder = Color.decode("#DBABF7"); + errorBackground = Color.decode("#FF0000"); + errorText = Color.decode("#FFFFFF"); + errorBorder = Color.decode("#FF0000"); } /** @@ -331,5 +339,57 @@ public class Theme { this.secondaryButtonBorder = secondaryButtonBorder; } - private File theme; + /** + * Gets the error background color. + * + * @return the error background color + */ + public Color getErrorBackground() { + return errorBackground; + } + + /** + * Sets the error background color. + * + * @param errorBackground the error background color + */ + public void setErrorBackground(Color errorBackground) { + this.errorBackground = errorBackground; + } + + /** + * Gets the error text color. + * + * @return the error text color + */ + public Color getErrorText() { + return errorText; + } + + /** + * Sets the error text color. + * + * @param errorText the error text color + */ + public void setErrorText(Color errorText) { + this.errorText = errorText; + } + + /** + * Gets the error border color. + * + * @return the error border color + */ + public Color getErrorBorder() { + return errorBorder; + } + + /** + * Sets the error border color. + * + * @param errorBorder the error border color + */ + public void setErrorBorder(Color errorBorder) { + this.errorBorder = errorBorder; + } }