wrote thorough documentation and made minor fixes for highlighting/selection

This commit is contained in:
Josh Ashton
2024-03-25 13:10:31 -06:00
parent 0409a8f41d
commit 600c215b7d
12 changed files with 625 additions and 376 deletions
+178 -88
View File
@@ -1,17 +1,16 @@
package gui;
import java.awt.Dimension;
// GUI imports
import javax.swing.JPanel;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.event.KeyAdapter;
// Event & action imports
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.Box;
// Processing & backend imports
import gui.backend.Cell;
import gui.backend.Settings;
import gui.backend.SudokuChecker;
@@ -22,6 +21,8 @@ import gui.backend.SudokuChecker;
* It contains all other related GUI components, and is the main JPanel for
* the board. Accessible information from the Board includes:
* - the current state of the board, including filled values and notes;
*
* TODO: Future features:
* - the current difficulty;
* - the current time since the puzzle was started;
* - the current hints available; and
@@ -36,9 +37,41 @@ public class Board extends JPanel {
private CellGUI selected;
/**
* Create a new Board object with the default styling from settings.
* Create a new Board object.
*
* Board requires a Settings object for styling, sizing, and for different
* toggleable features.
*
* A 2D Cell array is required to abstract the data away from the Board,
* and the GUI in general.
*
* @param s
* @param grid
*/
public Board(Settings s, Cell[][] grid) {
super(new GridLayout(9, 9));
this.s = s;
this.grid = grid;
solvedGrid = new SudokuChecker(Cell.copyGrid(grid)).getSolution();
style();
createBoard();
}
/**
* Create a new Board object.
*
* Board requires a Settings object for styling, sizing, and for different
* toggleable features.
*
* A 2D Cell array is required to abstract the data away from the Board,
* and the GUI in general.
*
* A SudokuChecker is required if Settings.getAutoCheckValues() is true.
*
* @param s
* @param grid
* @param sc
*/
public Board(Settings s, Cell[][] grid, SudokuChecker sc) {
super(new GridLayout(9, 9));
@@ -51,48 +84,6 @@ public class Board extends JPanel {
createBoard();
}
private KeyListener createKeyListener() {
return new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {}
@Override
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();
}
};
}
/**
* Get the grid of the Board.
*
@@ -137,6 +128,8 @@ public class Board extends JPanel {
* @param cell
*/
private void select(Cell cell) {
// Select the CellGUI objects in the same row and column, but not in
// the same box.
for(int n = 0; n < 9; n++) {
int row = cell.getRow();
int col = cell.getCol();
@@ -148,18 +141,22 @@ public class Board extends JPanel {
(gridGUI[row][n].cell.getBox() ==
cell.getBox());
// Handling edge cases where the column or row are in the box.
if(colInSameBox && !rowInSameBox) {
gridGUI[row][n].select();
continue;
} else if(!colInSameBox && rowInSameBox) {
gridGUI[n][col].select();
continue;
// Skip the cell if it is in the same box.
} else if(colInSameBox && rowInSameBox) continue;
gridGUI[n][col].select();
gridGUI[row][n].select();
}
// Select every cell in the box.
int boxRow = cell.getRow() / 3;
int boxCol = cell.getCol() / 3;
for(int n = 0; n < 3; n++) {
@@ -177,6 +174,12 @@ public class Board extends JPanel {
/**
* Create the Board Panel with the appropriate cells.
*
* Every CellGUI object is created with the appropriate Cell object and
* Settings object. The CellGUI objects are then added to the Board Panel.
*
* Each CellGUI object is also given a MouseListener to handle user input,
* and a KeyListener to handle keyboard input if/when the cell is selected.
*/
private void createBoard() {
if(s.getAutoFillNotes()) grid = sc.getPossibleValues(grid);
@@ -184,50 +187,137 @@ public class Board extends JPanel {
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
CellGUI cell = new CellGUI(
grid[i][j],
s
);
cell.addMouseListener(new MouseListener() {
@Override
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;
}
// Create a new CellGUI object with the appropriate Cell object.
CellGUI cell = new CellGUI(grid[i][j], s);
select(cell.cell);
selected = cell;
selected.requestFocusInWindow();
selected.addKeyListener(createKeyListener());
} else if(e.getButton() == 3) {
cell.setNoteMode();
return;
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {
cell.select();
}
@Override
public void mouseExited(MouseEvent e) {
cell.select();
}
});
// Add a MouseListener to the cell.
cell.addMouseListener(createMouseListener(cell));
// Add the CellGUI object to the Board Panel.
gridGUI[i][j] = cell;
add(gridGUI[i][j]);
}
}
}
/**
* Create a MouseListener for CellGUI objects.
*
* This MouseListener listens for mouse clicks, and selects the cell
* if it is clicked. If the cell is already selected, it is deselected.
*
* Additionally, adds hover highlighting for the cell.
*
* mouseClicked() is implemented for cell selection functionality.
* mouseEntered() and mouseExited() are implemented for hover functionality.
* Other methods are not implemented.
*
* @param cell
* @return MouseListener
*/
private MouseListener createMouseListener(CellGUI cell) {
return new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
// Left click
if(e.getButton() == 1) {
// Deselect the already selected cell.
if(selected != null) select(selected.cell);
// Deselect the cell if it is already selected.
if(selected == cell) {
selected = null;
return;
}
// Select the cell and add a KeyListener for input.
select(cell.cell);
selected = cell;
selected.requestFocusInWindow();
selected.addKeyListener(createKeyListener());
// Right click
} else if(e.getButton() == 3) {
cell.setNoteMode();
return;
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {
if(selected == null) cell.select();
}
@Override
public void mouseExited(MouseEvent e) {
if(selected == null) cell.select();
}
};
}
/**
* Create a KeyListener for CellGUI objects.
*
* This KeyListener listens for key releases, and updates the
* selected cell with additional possible values or a new value.
*
* Only keyReleased is implemented, as keyTyped does not allow for
* certain features regarding key codes.
*
* @return KeyListener
*/
private KeyListener createKeyListener() {
return new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {
// If a CellGUI is not selected, do nothing.
if(selected == null) return;
char key = e.getKeyChar();
Cell c = selected.cell;
// If the Cell is an initial value read from the starting file
// then skip it.
if(c.isInitValue()) return;
// Backspace support for removing values.
if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
selected.removeValue();
selected.repaint();
selected.revalidate();
return;
}
// Only allow digits to be entered.
if(!Character.isDigit(key)) return;
// Enter the value from the keyboard.
if(!selected.isInNotesMode()) {
// Set the value of the cell and check if it is correct.
if(s.getAutoCheckValues()) {
int row = c.getRow();
int col = c.getCol();
int expected = solvedGrid[row][col].getValue();
selected.setValue(Character.getNumericValue(key), expected);
// Set the value of the cell.
} else selected.setValue(Character.getNumericValue(key));
// Add the possible value to the cell.
} else selected.addPossibleValue(Character.getNumericValue(key));
selected.repaint();
selected.revalidate();
}
};
}
}