From 56f4fed7bcfcb9dfe4a1dd2377a0954107f4ad85 Mon Sep 17 00:00:00 2001 From: Josh Ashton Date: Sun, 24 Mar 2024 15:44:33 -0600 Subject: [PATCH] added keyboard input for cells when not in noteMode. noteMode is toggled with a right mouse click, and cells can be selected with left click --- src/App.java | 2 + src/gui/Board.java | 266 +++++++---------------------- src/gui/CellGUI.java | 224 ++++++++++++++++++++++++ src/gui/Nav.java | 55 ++++-- src/gui/{ => backend}/Cell.java | 2 +- src/gui/backend/Settings.java | 2 +- src/gui/backend/SudokuChecker.java | 2 - 7 files changed, 331 insertions(+), 222 deletions(-) create mode 100644 src/gui/CellGUI.java rename src/gui/{ => backend}/Cell.java (99%) diff --git a/src/App.java b/src/App.java index e792983..9ef280a 100644 --- a/src/App.java +++ b/src/App.java @@ -3,6 +3,8 @@ import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.BorderLayout; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; // Project imports import gui.*; diff --git a/src/gui/Board.java b/src/gui/Board.java index a108e49..39acf2b 100644 --- a/src/gui/Board.java +++ b/src/gui/Board.java @@ -1,19 +1,19 @@ package gui; -import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.LayoutManager; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.border.LineBorder; import javax.swing.Box; -import javax.swing.JLabel; +import gui.backend.Cell; import gui.backend.Settings; import gui.backend.SudokuChecker; -import gui.backend.Theme; /** * The Board class represents the Sudoku board in the GUI. @@ -30,7 +30,6 @@ public class Board extends JPanel { private Settings s; private Cell[][] grid; private CellGUI[][] gridGUI; - private Box[][] boxGUI = new Box[3][3]; private SudokuChecker sc; private CellGUI selected; @@ -48,6 +47,36 @@ public class Board extends JPanel { createBoard(); } + 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)); + } + + @Override + public void keyPressed(KeyEvent e) {} + + @Override + public void keyReleased(KeyEvent e) {} + }; + } + /** * Get the grid of the Board. * @@ -66,6 +95,9 @@ public class Board extends JPanel { */ public void setGrid(Cell[][] grid) { this.grid = grid; + gridGUI = null; + selected = null; + removeAll(); createBoard(); repaint(); @@ -143,210 +175,44 @@ public class Board extends JPanel { ); cell.addMouseListener(new MouseListener() { @Override - public void mouseClicked(java.awt.event.MouseEvent e) { - if(selected != null) select(selected.cell); - select(cell.cell); - selected = cell; + public void mouseClicked(MouseEvent e) { + // Left click + if(e.getButton() == 1) { + if(selected != null) select(selected.cell); + + // Deselect the cell if it is already selected. + if(selected == cell) { + selected = null; + return; + } + + select(cell.cell); + selected = cell; + selected.requestFocusInWindow(); + selected.addKeyListener(createKeyListener()); + } else if(e.getButton() == 3) { + cell.setNoteMode(); + return; + } } @Override - public void mousePressed(java.awt.event.MouseEvent e) {} + public void mousePressed(MouseEvent e) {} @Override - public void mouseReleased(java.awt.event.MouseEvent e) {} + public void mouseReleased(MouseEvent e) {} @Override - public void mouseEntered(java.awt.event.MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + cell.select(); + } @Override - public void mouseExited(java.awt.event.MouseEvent e) {} + public void mouseExited(MouseEvent e) { + cell.select(); + } }); + gridGUI[i][j] = cell; add(gridGUI[i][j]); } } } - - /** - * GUI representation of a single cell in the Sudoku grid. - */ - private class CellGUI extends JPanel { - // GUI fields - private JPanel internalPanel = new JPanel(new BorderLayout()); - private GridLayout noteLayout = new GridLayout(3, 3); - private GridLayout valueLayout = new GridLayout(0, 1); - private JLabel valueLabel; - private Note[] notesLabels = new Note[9]; - private Theme theme; - - // Data fields - private Cell cell; - private boolean noteMode; // true to display notes, or display value - private boolean selected = false; // true if the cell is selected - - /** - * Create a new CellGUI object with the given single cell. - * - * Start blank cells in notes mode if startInNotesOrValueMode is true. - * - * @param cell - * @param startInNotesOrValueMode - */ - public CellGUI(Cell cell, boolean noteMode, Theme theme) { - super(); - this.cell = cell; - this.noteMode = !noteMode; // Flip for use with setNoteMode() - this.theme = theme; - selected = false; - setLayout(valueLayout); - valueLabel = new JLabel("", SwingConstants.CENTER); - style(); - - // Populate the cell with the appropriate value or notes. - if(cell.getValue() == 0) { - internalPanel.setLayout(noteLayout); - generateNotes(); - } else { - valueLabel.setText(Integer.toString(cell.getValue())); - internalPanel.setLayout(valueLayout); - internalPanel.add(valueLabel); - } - add(internalPanel); - } - - /** - * Get the value of the underlying Cell object. - * - * @return int - */ - public int getValue() { - return cell.getValue(); - } - - /** - * Set the value of the underlying Cell object. - * - * @param value - */ - public void setValue(int value) { - cell.setValue(value); - // Update the GUI with the actual Cell value (unchanged if invalid) - valueLabel.setText(Integer.toString(cell.getValue())); - } - - /** - * Set the possible values of the underlying Cell object. - * - * @param value - */ - public void setPossibleValues(int[] values) { - cell.setPossibleValues(values); - } - - /** - * Add a possible value to the cell. - * - * @param value - */ - public void addPossibleValue(int value) { - cell.addPossibleValue(value); - setNoteMode(); - } - - /** - * Remove a possible value from the cell. - * - * @return - */ - public void removePossibleValue(int value) { - cell.removePossibleValue(value); - } - - /** - * Get the possible values of the underlying Cell object. - * - * @return int[] - */ - public int[] getPossibleValues() { - return cell.getPossibleValues(); - } - - /** - * Toggle the mode of the cell between value and note, including the - * layout of the cell. - * - * If the current mode is value, switch to note mode, and vice versa. - */ - public void setNoteMode() { - if(cell.isInitValue()) return; - - internalPanel.removeAll(); - if(noteMode) { - internalPanel.setLayout(valueLayout); - internalPanel.add(valueLabel); - } else { - internalPanel.setLayout(noteLayout); - generateNotes(); - } - - internalPanel.repaint(); - internalPanel.revalidate(); - repaint(); - revalidate(); - - noteMode = !noteMode; - } - - 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)); - } - - repaint(); - revalidate(); - } - - /** - * Style the cell with the appropriate colors from the theme. - */ - private void style() { - setBackground(theme.getPrimaryBackground()); - setForeground(theme.getPrimaryText()); - internalPanel.setBackground(theme.getPrimaryBackground()); - internalPanel.setForeground(theme.getPrimaryText()); - valueLabel.setForeground(theme.getPrimaryText()); - setBorder(new LineBorder(theme.getPrimaryBorder(), 2)); - } - - /** - * Create the GUI components for the cell. - */ - private void generateNotes() { - int[] possibleValues = cell.getPossibleValues(); - if(possibleValues.length == 0) { - for(int i = 0; i < 9; i++) { - notesLabels[i] = new Note("", theme); - internalPanel.add(notesLabels[i]); - } - return; - } - - int index = 0; - // Add the noted possible values to the cell. - for(int i = 1; i <= 9 && index < possibleValues.length; i++) { - if(possibleValues[index] == i) { - notesLabels[i - 1] = new Note(Integer.toString(i), theme); - index++; - } else - notesLabels[i - 1] = new Note("", theme); - internalPanel.add(notesLabels[i - 1]); - } - } - } } \ No newline at end of file diff --git a/src/gui/CellGUI.java b/src/gui/CellGUI.java new file mode 100644 index 0000000..4a7c418 --- /dev/null +++ b/src/gui/CellGUI.java @@ -0,0 +1,224 @@ +package gui; + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.LineBorder; + +import gui.backend.Cell; +import gui.backend.Theme; + +/** + * GUI representation of a single cell in the Sudoku grid. + */ +class CellGUI extends JPanel { + // GUI fields + private JPanel internalPanel = new JPanel(new BorderLayout()); + private GridLayout noteLayout = new GridLayout(3, 3); + private GridLayout valueLayout = new GridLayout(0, 1); + private JLabel valueLabel; + private Note[] notesLabels = new Note[9]; + private Theme theme; + + // Data fields + Cell cell; + private boolean noteMode; // true to display notes, or display value + private boolean selected = false; // true if the cell is selected + + /** + * Create a new CellGUI object with the given single cell. + * + * Start blank cells in notes mode if startInNotesOrValueMode is true. + * + * @param cell + * @param startInNotesOrValueMode + */ + public CellGUI(Cell cell, boolean noteMode, Theme theme) { + super(); + this.cell = cell; + this.noteMode = !noteMode; // Flip for use with setNoteMode() + this.theme = theme; + selected = false; + setFocusable(true); + setLayout(valueLayout); + valueLabel = new JLabel("", SwingConstants.CENTER); + style(); + + // Populate the cell with the appropriate value or notes. + if(cell.getValue() == 0) { + internalPanel.setLayout(noteLayout); + generateNotes(); + } else { + valueLabel.setText(Integer.toString(cell.getValue())); + internalPanel.setLayout(valueLayout); + internalPanel.add(valueLabel); + } + add(internalPanel); + } + + /** + * Get the value of the underlying Cell object. + * + * @return int + */ + public int getValue() { + return cell.getValue(); + } + + /** + * Set the value of the underlying Cell object. + * + * @param value + */ + public void setValue(int value) { + cell.setValue(value); + // Update the GUI with the actual Cell value (unchanged if invalid) + valueLabel.setText(Integer.toString(cell.getValue())); + internalPanel.repaint(); + internalPanel.revalidate(); + } + + /** + * Set the possible values of the underlying Cell object. + * + * @param value + */ + public void setPossibleValues(int[] values) { + cell.setPossibleValues(values); + } + + /** + * Add a possible value to the cell. + * + * @param value + */ + public void addPossibleValue(int value) { + cell.addPossibleValue(value); + setNoteMode(); + } + + /** + * Remove a possible value from the cell. + * + * @return + */ + public void removePossibleValue(int value) { + cell.removePossibleValue(value); + } + + /** + * Get the possible values of the underlying Cell object. + * + * @return int[] + */ + public int[] getPossibleValues() { + return cell.getPossibleValues(); + } + + /** + * Get the status of the cell in notes mode. + * + * @return + */ + public boolean isInNotesMode() { + return noteMode; + } + + /** + * Toggle the mode of the cell between value and note, including the + * layout of the cell. + * + * If the current mode is value, switch to note mode, and vice versa. + */ + public void setNoteMode() { + if(cell.isInitValue()) return; + + internalPanel.removeAll(); + if(noteMode) { + internalPanel.setLayout(valueLayout); + internalPanel.add(valueLabel); + } else { + internalPanel.setLayout(noteLayout); + generateNotes(); + } + + internalPanel.repaint(); + internalPanel.revalidate(); + repaint(); + revalidate(); + + noteMode = !noteMode; + } + + 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)); + + for(int i = 0; i < 9; i++) { + if(notesLabels[i] != null) { + notesLabels[i].setBackground(theme.getSecondaryBackground()); + notesLabels[i].setForeground(theme.getSecondaryText()); + } + } + } + + repaint(); + revalidate(); + } + + /** + * Style the cell with the appropriate colors from the theme. + */ + private void style() { + setBackground(theme.getPrimaryBackground()); + setForeground(theme.getPrimaryText()); + internalPanel.setBackground(theme.getPrimaryBackground()); + internalPanel.setForeground(theme.getPrimaryText()); + valueLabel.setForeground(theme.getPrimaryText()); + setBorder(new LineBorder(theme.getPrimaryBorder(), 2)); + + for(int i = 0; i < 9; i++) { + if(notesLabels[i] != null) { + notesLabels[i].setBackground(theme.getPrimaryBackground()); + notesLabels[i].setForeground(theme.getPrimaryText()); + } + } + } + + /** + * Create the GUI components for the cell. + */ + private void generateNotes() { + int[] possibleValues = cell.getPossibleValues(); + if(possibleValues.length == 0) { + for(int i = 0; i < 9; i++) { + notesLabels[i] = new Note("", theme); + internalPanel.add(notesLabels[i]); + } + return; + } + + int index = 0; + // Add the noted possible values to the cell. + for(int i = 1; i <= 9 && index < possibleValues.length; i++) { + if(possibleValues[index] == i) { + notesLabels[i - 1] = new Note(Integer.toString(i), theme); + index++; + } else + notesLabels[i - 1] = new Note("", theme); + internalPanel.add(notesLabels[i - 1]); + } + } +} \ No newline at end of file diff --git a/src/gui/Nav.java b/src/gui/Nav.java index 35fa451..cc6a348 100644 --- a/src/gui/Nav.java +++ b/src/gui/Nav.java @@ -1,10 +1,13 @@ package gui; import java.util.Scanner; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.io.File; import java.io.FileNotFoundException; import javax.swing.JPanel; +import gui.backend.Cell; import gui.backend.Settings; import gui.backend.SudokuChecker; @@ -163,25 +166,41 @@ public class Nav extends JPanel { fileOptions.addItem("Save"); fileOptions.addItem("Exit"); - // Add an ActionListener to the ComboBox to handle the user's selection. - fileOptions.addActionListener(e -> { - String selected = (String) fileOptions.getSelectedItem(); - switch(selected) { - case "New": - newFile(); - break; - case "Open": - openFile(); - break; - case "Save": - saveFile(); - break; - case "Exit": - System.exit(0); - break; - default: - System.out.println("Invalid selection."); + fileOptions.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + int selected = fileOptions.getSelectedIndex(); + switch(selected) { + case 0: + newFile(); + break; + case 1: + openFile(); + break; + case 2: + saveFile(); + break; + case 3: + System.exit(0); + break; + default: + System.out.println("Invalid selection."); + } } + + @Override + public void mousePressed(MouseEvent e) {} + + @Override + public void mouseReleased(MouseEvent e) {} + + @Override + public void mouseEntered(MouseEvent e) { + fileOptions.showPopup(); + } + + @Override + public void mouseExited(MouseEvent e) {} }); return fileOptions; diff --git a/src/gui/Cell.java b/src/gui/backend/Cell.java similarity index 99% rename from src/gui/Cell.java rename to src/gui/backend/Cell.java index a36a202..6ba91d6 100644 --- a/src/gui/Cell.java +++ b/src/gui/backend/Cell.java @@ -1,4 +1,4 @@ -package gui; +package gui.backend; /** * A Cell represents a single cell in the Sudoku grid. * This helper class is used to store the row, column, value, and possible values for a cell. diff --git a/src/gui/backend/Settings.java b/src/gui/backend/Settings.java index 5621090..2d2994b 100644 --- a/src/gui/backend/Settings.java +++ b/src/gui/backend/Settings.java @@ -346,7 +346,7 @@ public class Settings { cellGUIStartMode = false; defaultOpenState = 0; theme = new Theme(new File(appDirectory + "default.theme")); - autoFillNotes = false; + autoFillNotes = true; updateSettingsFile(); } diff --git a/src/gui/backend/SudokuChecker.java b/src/gui/backend/SudokuChecker.java index be25be7..e2641ae 100644 --- a/src/gui/backend/SudokuChecker.java +++ b/src/gui/backend/SudokuChecker.java @@ -1,8 +1,6 @@ package gui.backend; import java.util.ArrayList; -import gui.Cell; - /** * The SudokuChecker class is responsible for calculating the solution to * a given Sudoku puzzle.