From e1d79f4e3796d6e34a639293ef2cc8aa69207e88 Mon Sep 17 00:00:00 2001 From: Josh Ashton Date: Fri, 22 Mar 2024 08:32:09 -0600 Subject: [PATCH] basic board implementation, connected to file inputs from user. additionally expanded available settings and created structure for theming. --- src/App.java | 17 +- src/gui/Board.java | 165 ++++++++++++++++- src/gui/Nav.java | 39 +++- src/gui/backend/Settings.java | 81 +++++++-- src/gui/backend/Theme.java | 333 ++++++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 21 deletions(-) create mode 100644 src/gui/backend/Theme.java diff --git a/src/App.java b/src/App.java index 321b190..254f12a 100644 --- a/src/App.java +++ b/src/App.java @@ -1,13 +1,12 @@ +// GUI imports import javax.swing.JFrame; import javax.swing.UIManager; - -import gui.Nav; -import gui.Root; -import gui.backend.Settings; -import gui.backend.SudokuChecker; - import java.awt.BorderLayout; +// Project imports +import gui.*; +import gui.backend.*; + /** * Runner class for the Sudoku App, managing the GUI and coordinating * internal logic. @@ -15,6 +14,7 @@ import java.awt.BorderLayout; public class App extends JFrame { // GUI fields private Nav nav; + private Board board; // Data fields private Settings s; @@ -25,8 +25,10 @@ public class App extends JFrame { public App() { super("Sudoku"); s = new Settings(); - nav = new Nav(s, false); initSetup(); + nav = new Nav(s, false); + board = new Board(s, nav.getLoadedGrid()); + nav.setBoard(board); add(createApp()); } @@ -68,6 +70,7 @@ public class App extends JFrame { private Root createApp() { Root root = new Root(s); root.add(nav); + root.add(board); return root; } diff --git a/src/gui/Board.java b/src/gui/Board.java index 01f64fc..ee1b030 100644 --- a/src/gui/Board.java +++ b/src/gui/Board.java @@ -1,8 +1,10 @@ package gui; import java.awt.GridLayout; +import java.awt.LayoutManager; import javax.swing.JPanel; +import javax.swing.JLabel; import gui.backend.Settings; @@ -20,6 +22,7 @@ import gui.backend.Settings; public class Board extends JPanel { private Settings s; private Cell[][] grid; + private Cell selected; /** * Create a new Board object with the default styling from settings. @@ -31,6 +34,31 @@ public class Board extends JPanel { this.s = s; this.grid = grid; style(); + createBoard(); + } + + /** + * Get the grid of the Board. + * + * @return + */ + public Cell[][] getGrid() { + return grid; + } + + /** + * Set the grid of the Board to the given grid. + * + * Intended to be used when loading a new puzzle. + * + * @param grid + */ + public void setGrid(Cell[][] grid) { + this.grid = grid; + removeAll(); + createBoard(); + repaint(); + revalidate(); } /** @@ -41,6 +69,139 @@ public class Board extends JPanel { } private void createBoard() { - // TODO: Create the board with the given grid. + for(int i = 0; i < 9; i++) { + for(int j = 0; j < 9; j++) { + CellGUI cell = new CellGUI(grid[i][j], s.getCellGUIStartMode()); + add(cell); + } + } } -} + + /** + * GUI representation of a single cell in the Sudoku grid. + */ + private class CellGUI extends JPanel { + private static GridLayout noteLayout = new GridLayout(3, 3); + private static GridLayout valueLayout = new GridLayout(1, 1); + + private Cell cell; + private JLabel valueLabel; + private JLabel[] notesLabels; + private boolean notes; // true for notes, false for value + private boolean startInNotesOrValueMode; + + /** + * 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 startInNotesOrValueMode) { + super(); + this.cell = cell; + this.startInNotesOrValueMode = startInNotesOrValueMode; + + // Populate the cell with the appropriate value or notes. + if(cell.getValue() == 0) { + generateNotes(); + + if(startInNotesOrValueMode) { + setLayout(noteLayout); + notes = true; + } else { + setLayout(valueLayout); + notes = false; + valueLabel = new JLabel(""); + add(valueLabel); + } + } else { + valueLabel = new JLabel(Integer.toString(cell.getValue())); + add(valueLabel); + } + } + + /** + * Get the value of the underlying Cell object. + * + * @return int + */ + public int getValue() { + return cell.getValue(); + } + + /** + * Add a possible value to the cell. + * + * @param value + */ + public void addPossibleValue(int value) { + cell.addPossibleValue(value); + } + + /** + * 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. + */ + public void setNoteMode() { + if(notes) { + removeAll(); + setLayout(valueLayout); + repaint(); + revalidate(); + + add(valueLabel); + } else { + removeAll(); + setLayout(noteLayout); + repaint(); + revalidate(); + + generateNotes(); + } + + notes = !notes; + } + + /** + * Create the GUI components for the cell. + */ + private void generateNotes() { + if(cell.getPossibleValues().length == 0) { + notesLabels = new JLabel[9]; + return; + } + + // Add the noted possible values to the cell. + int index = 0; + for(int i = 1; i <= 9; i++) { + if(cell.getPossibleValues()[index] == i) { + notesLabels[index] = new JLabel(Integer.toString(i)); + index++; + } else { + notesLabels[index] = new JLabel(""); + } + add(notesLabels[index]); + } + } + } +} \ No newline at end of file diff --git a/src/gui/Nav.java b/src/gui/Nav.java index ed0b07c..8d8f63f 100644 --- a/src/gui/Nav.java +++ b/src/gui/Nav.java @@ -27,6 +27,7 @@ import javax.swing.JButton; public class Nav extends JPanel { private Settings s; private File f; + private Board b; private Cell[][] grid; /** @@ -77,8 +78,37 @@ public class Nav extends JPanel { openFile(inputFilename); // Otherwise, create the GUI for the Nav bar. - } else createGUI(); + } else { + createGUI(); + // TODO: Need to implement the default file options in Settings. + switch(s.getDefaultOpenState()) { + case 0: + openFile("input"); + break; + case 1: + openFile("medium"); + break; + case 2: + openFile("hard"); + break; + default: + System.out.println("Invalid default file option."); + } + } + } + + /** + * Set the Board object for the Nav bar. + * + * This is necessary for the Nav bar to be able to interact with the + * Board object and update the grid with file I/O operations. This + * shouldn't be necessary to change once the program is running. + * + * @param b + */ + public void setBoard(Board b) { + this.b = b; } /** @@ -174,6 +204,7 @@ public class Nav extends JPanel { f = fc.getSelectedFile(); createGrid(f); } + b.setGrid(grid); } /** @@ -196,6 +227,11 @@ public class Nav extends JPanel { f.getAbsolutePath() ); + grid = new Cell[9][9]; + for(int i = 0; i < 9; i++) + for(int j = 0; j < 9; j++) + grid[i][j] = new Cell(i, j, 0); + return; } @@ -281,6 +317,7 @@ public class Nav extends JPanel { ); e.printStackTrace(); + grid = new Cell[9][9]; return; } } diff --git a/src/gui/backend/Settings.java b/src/gui/backend/Settings.java index 3ccc88a..764213e 100644 --- a/src/gui/backend/Settings.java +++ b/src/gui/backend/Settings.java @@ -44,6 +44,12 @@ public class Settings { // Resizable setting private boolean resizable; + // Cell GUI Start Mode + private boolean cellGUIStartMode; + + // Default open state for the application. + private int defaultOpenState; + /** * Create a new Settings object, initializing the default settings * or reading in the settings from settings file if it exists. @@ -174,28 +180,78 @@ public class Settings { * * @return boolean */ - public boolean getResizable() { + public boolean getResizable() { return resizable; - } + } - /** - * Set the resizable setting and write it to the settings file. - * - * @param resizable - */ - public void setResizable(boolean resizable) { + /** + * Set the resizable setting and write it to the settings file. + * + * @param resizable + */ + public void setResizable(boolean resizable) { this.resizable = resizable; updateSettingsFile(); - } + } + + /** + * The Cell GUI Start Mode is a configurable setting, where the user can + * specify whether the cell GUI should start in notes mode or value mode. + * + * By default, the cell GUI starts in value mode, ie. returns false. + * Set to true to start in notes mode. + * + * @return boolean + */ + public boolean getCellGUIStartMode() { + return cellGUIStartMode; + } + + /** + * Set the cell GUI start mode and write it to the settings file. + * + * @param cellGUIStartMode + */ + public void setCellGUIStartMode(boolean cellGUIStartMode) { + this.cellGUIStartMode = cellGUIStartMode; + updateSettingsFile(); + } + + /** + * The default open state for the application. The following values + * are accepted: + * 0 - Open the default easy.sdku file, default behavior; + * 1 - Open the last opened .sdku file; + * 2 - Open a new, blank .sdku file; and + * 3 - Open a new, random .sdku file. + * + * NOTE: The value 2 is not yet supported. + * + * @return + */ + public int getDefaultOpenState() { + return defaultOpenState; + } + + /** + * Set the default open state for the application and write it to the + * settings file. + * + * @param defaultOpenState + */ + public void setDefaultOpenState(int defaultOpenState) { + this.defaultOpenState = defaultOpenState; + updateSettingsFile(); + } /** * Open and write the settings to the settings file. * * This method is called whenever a setting is updated. */ - private void updateSettingsFile() { + private void updateSettingsFile() { // TODO: Write the settings to the settings file. - } + } /** * Set the default settings for the application and write them to the @@ -221,6 +277,9 @@ public class Settings { dimension = new Dimension(750, 550); resizable = false; + cellGUIStartMode = false; + defaultOpenState = 0; + updateSettingsFile(); } diff --git a/src/gui/backend/Theme.java b/src/gui/backend/Theme.java new file mode 100644 index 0000000..c848bd7 --- /dev/null +++ b/src/gui/backend/Theme.java @@ -0,0 +1,333 @@ +package gui.backend; + +import java.awt.Color; +import java.io.File; + +/** + * The Theme class represents the color scheme for the GUI components. + * + * The Theme class reads a theme file and sets the colors for the GUI components. + * + * The Theme class provides the following color customizations: + * - primaryBackground + * - secondaryBackground + * - primaryText + * - secondaryText + * - primaryHighlight + * - secondaryHighlight + * - primaryBorder + * - secondaryBorder + * - primaryButton + * - secondaryButton + * - primaryButtonHighlight + * - secondaryButtonHighlight + * - primaryButtonBorder + * - secondaryButtonBorder + */ +import java.awt.Color; +import java.io.File; + +/** + * The Theme class represents a theme for the GUI components. + * It provides colors for various GUI elements. + */ +public class Theme { + private Color primaryBackground; + private Color secondaryBackground; + private Color primaryText; + private Color secondaryText; + private Color primaryHighlight; + private Color secondaryHighlight; + private Color primaryBorder; + private Color secondaryBorder; + private Color primaryButton; + private Color secondaryButton; + private Color primaryButtonHighlight; + private Color secondaryButtonHighlight; + private Color primaryButtonBorder; + private Color secondaryButtonBorder; + + /** + * Constructs a new Theme object with the given theme file. + * This object provides the colors for the GUI components. + * + * @param theme the theme file + */ + public Theme(File theme) { + this.theme = theme; + readTheme(); + } + + /** + * Reads the .theme file and sets the colors for the GUI components. + * Once complete, sets the theme for the GUI components. + */ + private void readTheme() { + // TODO: Read the .theme file + setTheme(); + } + + /** + * Sets the theme for the GUI components. + */ + private void setTheme() { + primaryBackground = Color.WHITE; + secondaryBackground = Color.LIGHT_GRAY; + primaryText = Color.BLACK; + secondaryText = Color.DARK_GRAY; + } + + /** + * Gets the primary background color. + * + * @return the primary background color + */ + public Color getPrimaryBackground() { + return primaryBackground; + } + + /** + * Sets the primary background color. + * + * @param primaryBackground the primary background color + */ + public void setPrimaryBackground(Color primaryBackground) { + this.primaryBackground = primaryBackground; + } + + /** + * Gets the secondary background color. + * + * @return the secondary background color + */ + public Color getSecondaryBackground() { + return secondaryBackground; + } + + /** + * Sets the secondary background color. + * + * @param secondaryBackground the secondary background color + */ + public void setSecondaryBackground(Color secondaryBackground) { + this.secondaryBackground = secondaryBackground; + } + + /** + * Gets the primary text color. + * + * @return the primary text color + */ + public Color getPrimaryText() { + return primaryText; + } + + /** + * Sets the primary text color. + * + * @param primaryText the primary text color + */ + public void setPrimaryText(Color primaryText) { + this.primaryText = primaryText; + } + + /** + * Gets the secondary text color. + * + * @return the secondary text color + */ + public Color getSecondaryText() { + return secondaryText; + } + + /** + * Sets the secondary text color. + * + * @param secondaryText the secondary text color + */ + public void setSecondaryText(Color secondaryText) { + this.secondaryText = secondaryText; + } + + /** + * Gets the primary highlight color. + * + * @return the primary highlight color + */ + public Color getPrimaryHighlight() { + return primaryHighlight; + } + + /** + * Sets the primary highlight color. + * + * @param primaryHighlight the primary highlight color + */ + public void setPrimaryHighlight(Color primaryHighlight) { + this.primaryHighlight = primaryHighlight; + } + + /** + * Gets the secondary highlight color. + * + * @return the secondary highlight color + */ + public Color getSecondaryHighlight() { + return secondaryHighlight; + } + + /** + * Sets the secondary highlight color. + * + * @param secondaryHighlight the secondary highlight color + */ + public void setSecondaryHighlight(Color secondaryHighlight) { + this.secondaryHighlight = secondaryHighlight; + } + + /** + * Gets the primary border color. + * + * @return the primary border color + */ + public Color getPrimaryBorder() { + return primaryBorder; + } + + /** + * Sets the primary border color. + * + * @param primaryBorder the primary border color + */ + public void setPrimaryBorder(Color primaryBorder) { + this.primaryBorder = primaryBorder; + } + + /** + * Gets the secondary border color. + * + * @return the secondary border color + */ + public Color getSecondaryBorder() { + return secondaryBorder; + } + + /** + * Sets the secondary border color. + * + * @param secondaryBorder the secondary border color + */ + public void setSecondaryBorder(Color secondaryBorder) { + this.secondaryBorder = secondaryBorder; + } + + /** + * Gets the primary button color. + * + * @return the primary button color + */ + public Color getPrimaryButton() { + return primaryButton; + } + + /** + * Sets the primary button color. + * + * @param primaryButton the primary button color + */ + public void setPrimaryButton(Color primaryButton) { + this.primaryButton = primaryButton; + } + + /** + * Gets the secondary button color. + * + * @return the secondary button color + */ + public Color getSecondaryButton() { + return secondaryButton; + } + + /** + * Sets the secondary button color. + * + * @param secondaryButton the secondary button color + */ + public void setSecondaryButton(Color secondaryButton) { + this.secondaryButton = secondaryButton; + } + + /** + * Gets the primary button highlight color. + * + * @return the primary button highlight color + */ + public Color getPrimaryButtonHighlight() { + return primaryButtonHighlight; + } + + /** + * Sets the primary button highlight color. + * + * @param primaryButtonHighlight the primary button highlight color + */ + public void setPrimaryButtonHighlight(Color primaryButtonHighlight) { + this.primaryButtonHighlight = primaryButtonHighlight; + } + + /** + * Gets the secondary button highlight color. + * + * @return the secondary button highlight color + */ + public Color getSecondaryButtonHighlight() { + return secondaryButtonHighlight; + } + + /** + * Sets the secondary button highlight color. + * + * @param secondaryButtonHighlight the secondary button highlight color + */ + public void setSecondaryButtonHighlight(Color secondaryButtonHighlight) { + this.secondaryButtonHighlight = secondaryButtonHighlight; + } + + /** + * Gets the primary button border color. + * + * @return the primary button border color + */ + public Color getPrimaryButtonBorder() { + return primaryButtonBorder; + } + + /** + * Sets the primary button border color. + * + * @param primaryButtonBorder the primary button border color + */ + public void setPrimaryButtonBorder(Color primaryButtonBorder) { + this.primaryButtonBorder = primaryButtonBorder; + } + + /** + * Gets the secondary button border color. + * + * @return the secondary button border color + */ + public Color getSecondaryButtonBorder() { + return secondaryButtonBorder; + } + + /** + * Sets the secondary button border color. + * + * @param secondaryButtonBorder the secondary button border color + */ + public void setSecondaryButtonBorder(Color secondaryButtonBorder) { + this.secondaryButtonBorder = secondaryButtonBorder; + } + + private File theme; +}