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

This commit is contained in:
Josh Ashton
2024-03-24 15:44:33 -06:00
parent e821ffa24b
commit 56f4fed7bc
7 changed files with 331 additions and 222 deletions
+2
View File
@@ -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.*;
+66 -200
View File
@@ -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]);
}
}
}
}
+224
View File
@@ -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]);
}
}
}
+37 -18
View File
@@ -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;
@@ -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.
+1 -1
View File
@@ -346,7 +346,7 @@ public class Settings {
cellGUIStartMode = false;
defaultOpenState = 0;
theme = new Theme(new File(appDirectory + "default.theme"));
autoFillNotes = false;
autoFillNotes = true;
updateSettingsFile();
}
-2
View File
@@ -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.