package carcassonne.view.secondary; import java.awt.GridBagConstraints; import java.util.ArrayList; import java.util.List; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.border.Border; import javax.swing.border.LineBorder; import carcassonne.control.MainController; import carcassonne.model.Player; import carcassonne.model.tile.Tile; import carcassonne.model.tile.TileType; import carcassonne.settings.GameSettings; import carcassonne.util.ImageLoadingUtil; import carcassonne.util.MouseClickListener; import carcassonne.view.main.MainGUI; /** * GUI class for the tile orientation. It lets the user look at the tile to place and rotate it both right and left. * @author Timur Saglam */ public class PreviewGUI extends SecondaryGUI { private static final long serialVersionUID = -5179683977081970564L; private static final int BOTTOM_SPACE = 5; private static final int VERTICAL_SPACE = 10; private static final double SELECTION_FACTOR = 0.9; private static final int SELECTION_BORDER_WIDTH = 3; private static final String TILE_TOOL_TIP = "Tile of type "; private final int selectionSize; private final int defaultSize; private JButton buttonRotateLeft; private JButton buttonRotateRight; private JButton buttonSkip; private List<JLabel> tileLabels; private List<Tile> tiles; private int selectionIndex; /** * Simple constructor which uses the constructor of the <code>SmallGUI</code>. * @param controller is the game controller. * @param ui is the main GUI. */ public PreviewGUI(MainController controller, MainGUI ui) { super(controller, ui); buildContent(); pack(); selectionSize = buttonSkip.getWidth() + buttonRotateLeft.getWidth() + buttonRotateRight.getWidth() - VERTICAL_SPACE; defaultSize = (int) (selectionSize * SELECTION_FACTOR); } /** * Returns the tile correlating to the selected tile label. * @return the tile. */ public Tile getSelectedTile() { return tiles.get(selectionIndex); } /** * If the UI is active, rotates the tile to the left. */ public void rotateLeft() { if (isVisible()) { tiles.get(selectionIndex).rotateLeft(); updateTileLabel(selectionIndex); } } /** * If the UI is active, rotates the tile to the right. */ public void rotateRight() { if (isVisible()) { tiles.get(selectionIndex).rotateRight(); updateTileLabel(selectionIndex); } } /** * Selects the next tile label above the current selected one. */ public void selectAbove() { if (selectionIndex > 0) { selectTileLabel(selectionIndex - 1); } } /** * Selects the next tile label below the current selected one. */ public void selectBelow() { if (selectionIndex + 1 < tiles.size()) { selectTileLabel(selectionIndex + 1); } } /** * Sets the tiles of the GUI to the tiles of the current player, updates the GUI and then makes it visible. Should be * called to show the GUI. * @param currentPlayer is the active player. */ public void setTiles(Player currentPlayer) { tiles.clear(); tiles.addAll(currentPlayer.getHandOfTiles()); setCurrentPlayer(currentPlayer); updatePreviewLabels(); showUI(); } @Override public void notifyChange() { super.notifyChange(); if (!tiles.isEmpty()) { updateTileLabel(selectionIndex); } } /** * Selects a specific tile label, increasing its size and adding a border. Resets the previous selection. * @param index is the index of the label. If the index is not valid nothing will happen. */ public void selectTileLabel(int index) { if (index < tiles.size()) { int oldSelection = selectionIndex; selectionIndex = index; updateTileLabel(index); updateTileLabel(oldSelection); } } // build the GUI content private void buildContent() { // create buttons: buttonSkip = new JButton(ImageLoadingUtil.SKIP.createHighDpiImageIcon()); buttonRotateLeft = new JButton(ImageLoadingUtil.LEFT.createHighDpiImageIcon()); buttonRotateRight = new JButton(ImageLoadingUtil.RIGHT.createHighDpiImageIcon()); // set tool tips: buttonSkip.setToolTipText("Don't place tile and skip turn"); buttonRotateLeft.setToolTipText("Rotate left"); buttonRotateRight.setToolTipText("Rotate right"); // set listeners: buttonSkip.addMouseListener((MouseClickListener) event -> controller.requestSkip()); buttonRotateLeft.addMouseListener((MouseClickListener) event -> rotateLeft()); buttonRotateRight.addMouseListener((MouseClickListener) event -> rotateRight()); // set constraints: GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.NONE; // add buttons: dialogPanel.add(buttonRotateLeft, constraints); dialogPanel.add(buttonSkip, constraints); dialogPanel.add(buttonRotateRight, constraints); // change constraints and add label: constraints.fill = GridBagConstraints.VERTICAL; constraints.gridy = 1; constraints.gridwidth = 3; ImageIcon defaultImage = new Tile(TileType.Null).getScaledIcon(50); tileLabels = new ArrayList<>(); tiles = new ArrayList<>(); for (int i = 0; i < GameSettings.MAXIMAL_TILES_ON_HAND; i++) { JLabel label = new JLabel(defaultImage); tileLabels.add(label); constraints.gridy++; final int index = i; label.addMouseListener((MouseClickListener) event -> selectTileLabel(index)); dialogPanel.add(label, constraints); } constraints.gridy++; dialogPanel.add(Box.createVerticalStrut(BOTTOM_SPACE), constraints); } // Updates the image of a specific tile label. private void updateTileLabel(int index) { boolean singleTile = tiles.size() == 1; boolean selected = index == selectionIndex; // is the label selected or not? ImageIcon icon = tiles.get(index).getScaledIcon(selected || singleTile ? selectionSize : defaultSize); tileLabels.get(index).setToolTipText(TILE_TOOL_TIP + tiles.get(index).getType().readableRepresentation()); tileLabels.get(index).setIcon(icon); tileLabels.get(index).setBorder(selected && !singleTile ? createSelectionBorder() : null); } // Resets the selection index and adapts the tile labels to the given amount of tiles. private void updatePreviewLabels() { selectionIndex = 0; for (int i = tiles.size(); i < GameSettings.MAXIMAL_TILES_ON_HAND; i++) { tileLabels.get(i).setVisible(false); } for (int i = 0; i < tiles.size(); i++) { tileLabels.get(i).setVisible(true); updateTileLabel(i); } pack(); } // Creates the selection border. The color is always up to date. private Border createSelectionBorder() { return new LineBorder(currentPlayer.getColor().textColor(), SELECTION_BORDER_WIDTH); } }