From 1b5564760aff0b99adc060cc1c29861d585c3b65 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 18 Sep 2021 14:10:51 +0100 Subject: [PATCH] Latest UI changes Latest version of the UI with expandable panel with options. Options include Release Date, Card Art Preference, and Block Filter (if available). This new UI update also fixes some small imperfections in the previous version, with better management of spacing. Signed-off-by: leriomaggio --- .../forge/screens/deckeditor/DeckImport.java | 314 +++++++++++++----- 1 file changed, 226 insertions(+), 88 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java index 9c885a7ebd4..426c1e260a7 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java @@ -18,13 +18,12 @@ package forge.screens.deckeditor; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowEvent; +import java.awt.event.*; import java.util.List; import javax.swing.*; import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.HyperlinkEvent; @@ -46,6 +45,7 @@ import forge.model.FModel; import forge.screens.deckeditor.controllers.ACEditorBase; import forge.screens.deckeditor.controllers.CStatisticsImporter; import forge.screens.deckeditor.views.VStatisticsImporter; +import forge.toolbox.FComboBox; import forge.toolbox.*; import forge.util.Localizer; import forge.view.FDialog; @@ -175,22 +175,32 @@ public class DeckImport ex private final FHtmlViewer htmlOutput = new FHtmlViewer(DeckImport.HTML_WELCOME_TEXT); private final FScrollPane scrollInput = new FScrollPane(this.txtInput, false); private final FScrollPane scrollOutput = new FScrollPane(this.htmlOutput, false); - private final CardPicturePanel picturePreview = new CardPicturePanel(); + private final CardPicturePanel cardImagePreview = new CardPicturePanel(); private final FLabel cardPreviewLabel = new FLabel.Builder() - .text(String.format("%s", - Localizer.getInstance().getMessage("lblCardPreview"))) - .fontStyle(Font.BOLD).tooltip("").build(); - private final FButton cmdCancel = new FButton(Localizer.getInstance().getMessage("lblCancel")); + .text(Localizer.getInstance().getMessage("lblCardPreview")) + .fontSize(14).tooltip("").build(); + private final FButton cmdCancelButton = new FButton(Localizer.getInstance().getMessage("lblCancel")); - private final FButton cmdAccept; // Not initialised as label will be adaptive. + private final FButton cmdAcceptButton; // Not initialised as label will be adaptive. private final FCheckBox createNewDeckCheckbox = new FCheckBox(Localizer.getInstance().getMessage("lblNewDeckCheckbox"), false); private final DeckFormat deckFormat; - //don't need wrappers since skin can't change while this dialog is open + // Release Date private final FCheckBox dateTimeCheck = new FCheckBox(Localizer.getInstance().getMessage("lblUseOnlySetsReleasedBefore"), false); private final FComboBox monthDropdown = new FComboBox<>(); private final FComboBox yearDropdown = new FComboBox<>(); + // Card Art Preferences + private final FLabel cardArtPrefsLabel = new FLabel.Builder() + .text(Localizer.getInstance().getMessage("lblPreferredArt")) + .fontSize(14).build(); + private FComboBox cardArtPrefsComboBox; + private final FCheckBox cardArtPrefFilter = new FCheckBox(Localizer.getInstance().getMessage("lblPrefArtExpansionOnly"), false); + + // Block Format Filter + private final FCheckBox blockCheck = new FCheckBox(Localizer.getInstance().getMessage("lblUseBlockFilters"), false); + private final FComboBox blocksDropdown = new FComboBox<>(); + private final DeckImportController controller; private final ACEditorBase host; @@ -204,12 +214,15 @@ public class DeckImport ex GameType currentGameType = g.getGameType(); // get the game format with the same name of current game type (if any) GameFormat currentGameFormat = FModel.getFormats().get(currentGameType.name()); + GameFormat vintageGameFormat = FModel.getFormats().get("Vintage"); // default for constructed if (currentGameFormat == null) - currentGameFormat = FModel.getFormats().get("Vintage"); // default for constructed + currentGameFormat = vintageGameFormat; List allowedSetCodes = currentGameFormat.getAllowedSetCodes(); + if (allowedSetCodes == null || allowedSetCodes.isEmpty()) + allowedSetCodes = vintageGameFormat.getAllowedSetCodes(); this.controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, - currentDeckIsNotEmpty, allowedSetCodes, currentDeckFormat); - this.cmdAccept = new FButton(IMPORT_CARDS_CMD_LABEL); + currentDeckIsNotEmpty, allowedSetCodes, currentDeckFormat, blockCheck, blocksDropdown); + this.cmdAcceptButton = new FButton(IMPORT_CARDS_CMD_LABEL); this.host = g; initMainPanel(g, currentDeckIsNotEmpty, currentGameType); } @@ -223,6 +236,7 @@ public class DeckImport ex this.setPreferredSize(new Dimension(wWidth, wHeight)); this.setSize(wWidth, wHeight); + // Set Title String gameTypeName = String.format(" %s", currentGameType.name()); this.setTitle(Localizer.getInstance().getMessage("lblDeckImporterPanelTitle") + gameTypeName); @@ -231,49 +245,126 @@ public class DeckImport ex final FSkin.SkinColor foreColor = FSkin.getColor(FSkin.Colors.CLR_TEXT); - this.picturePreview.setOpaque(false); - this.picturePreview.setBorder(new EmptyBorder(2, 5, 2, 5)); - this.picturePreview.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT).getColor()); - resetCardPreviewPanel(); + // === INIT COMPONENTS + // == Scroll Input (Card List) this.scrollInput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), Localizer.getInstance().getMessage("lblCardListTitle"), foreColor)); this.scrollInput.setViewportBorder(BorderFactory.createLoweredBevelBorder()); - + // == Scroll Output (Decklist) this.scrollOutput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), Localizer.getInstance().getMessage("lblDecklistTitle"), foreColor)); this.scrollOutput.setViewportBorder(BorderFactory.createLoweredBevelBorder()); - + // == Stats Panel FPanel statsPanel = new FPanel(new BorderLayout()); statsPanel.add(VStatisticsImporter.instance().getMainPanel(), BorderLayout.CENTER); statsPanel.setOpaque(false); - + // == Card Preview + this.cardImagePreview.setOpaque(false); + this.cardImagePreview.setBorder(new EmptyBorder(2, 5, 2, 5)); + this.cardImagePreview.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT).getColor()); + resetCardImagePreviewPanel(); FPanel cardPreview = new FPanel(new MigLayout("fill")); - cardPreview.add(this.cardPreviewLabel, "cell 0 0, align left, w 100%, span 2"); - cardPreview.add(this.picturePreview, "cell 0 1, w 80%, h 70%, growy, pushy, ax c, span 2"); + cardPreview.add(this.cardPreviewLabel, "cell 0 0, align left, w 100%"); + cardPreview.add(this.cardImagePreview, "cell 0 1, w 70%, h 60%, growy, pushy, ax c"); + // == Options Panel + JPanel optionsPanel = new JPanel(new MigLayout("insets 10, gap 5, center, h 120!")); + final TitledBorder border = new TitledBorder(BorderFactory.createEtchedBorder(), "Options"); + border.setTitleColor(foreColor.getColor()); + optionsPanel.setBorder(border); + optionsPanel.setVisible(false); + optionsPanel.setOpaque(false); + FButton showOptionsButton = new FButton(Localizer.getInstance().getMessage("lblShowOptions")); + showOptionsButton.setFont(FSkin.getBoldFont(12)); + FButton hideOptionsButton = new FButton(Localizer.getInstance().getMessage("lblHideOptions")); + hideOptionsButton.setFont(FSkin.getBoldFont(12)); + + String optPanelConstrains = "w 130!, h 80!, left, insets 0"; + + // = (opt) Date filter + JPanel dateFilterPanel = new JPanel(new MigLayout(optPanelConstrains)); + dateFilterPanel.setOpaque(false); + dateFilterPanel.add(this.dateTimeCheck, "cell 0 0, w 40%, ax left"); + dateFilterPanel.add(this.monthDropdown, "cell 0 1, w 10%, ax left, split 2, pad 0 2 0 0"); + dateFilterPanel.add(this.yearDropdown, "cell 0 1, w 8%, ax left, split 2"); + + // (opt) Card Art Preference Filter + + final String latestOpt = Localizer.getInstance().getMessage("latestArtOpt"); + final String originalOpt = Localizer.getInstance().getMessage("originalArtOpt"); + final String [] choices = {latestOpt, originalOpt}; + this.cardArtPrefsComboBox = new FComboBox<>(choices); + final String selectedItem = StaticData.instance().cardArtPreferenceIsLatest() ? latestOpt : originalOpt; + this.cardArtPrefsComboBox.setSelectedItem(selectedItem); + this.cardArtPrefFilter.setSelected(StaticData.instance().isCoreExpansionOnlyFilterSet()); + + JPanel cardArtPanel = new JPanel(new MigLayout(optPanelConstrains)); + cardArtPanel.setOpaque(false); + cardArtPanel.add(this.cardArtPrefsLabel, "cell 0 0, w 25%, left, split 2"); + cardArtPanel.add(this.cardArtPrefsComboBox, "cell 0 0, w 10%, left, split 2"); + cardArtPanel.add(this.cardArtPrefFilter, "cell 0 1, w 15%, left"); + + // (opt) Block Filter + this.blocksDropdown.setEnabled(false); // not enabled by default + JPanel blockFilterPanel = new JPanel(new MigLayout(optPanelConstrains)); + blockFilterPanel.setOpaque(false); + blockFilterPanel.add(this.blockCheck, "cell 0 0, w 40%, ax left"); + blockFilterPanel.add(this.blocksDropdown, "cell 0 1, w 15%, ax right"); + + optionsPanel.add(hideOptionsButton, "w 150!, h 26!, cell 0 0, span 4, growx, left"); + optionsPanel.add(dateFilterPanel, "cell 0 2, w 100%, left"); + + if (this.controller.isBlockFormatsSupported()) { + optionsPanel.add(cardArtPanel, "cell 1 2, w 100%, left"); + optionsPanel.add(blockFilterPanel, "cell 2 2, w 100%, left"); + } else { + optionsPanel.add(cardArtPanel, "cell 1 2, w 100%, left"); + JPanel placeHolderPanel = new JPanel(new MigLayout(optPanelConstrains)); + placeHolderPanel.setOpaque(false); + optionsPanel.add(placeHolderPanel, "cell 2 2, w 100%, left"); + } + + // == Command buttons + JPanel cmdPanel = new JPanel(new MigLayout("insets 10, gap 5, right, h 40!")); + cmdPanel.setOpaque(false); + if (currentDeckIsNotEmpty) { + cmdPanel.add(this.createNewDeckCheckbox, "align l, split 3"); + cmdPanel.add(this.cmdAcceptButton, "w 150!, align r, h 26!, split 3"); + cmdPanel.add(this.cmdCancelButton, "w 150!, align r, h 26!, split 3"); + } else { + cmdPanel.add(this.cmdAcceptButton, "w 150!, align r, h 26!, split 2"); + cmdPanel.add(this.cmdCancelButton, "w 150!, align r, h 26!, split 2"); + } + + // === Assembling main UI component this.add(this.scrollInput, "cell 0 0, w 40%, growy, pushy, spany 2"); this.add(this.scrollOutput, "cell 1 0, w 60%, growy, pushy, spany 2"); - this.add(statsPanel, "cell 2 0, w 480:510:550, growy, pushy, ax c, spanx 2"); - this.add(cardPreview, "cell 2 1, w 480:510:550, h 65%, growy, pushy, ax c, spanx 2"); + this.add(statsPanel, "cell 2 0, w 480:510:550, growy, pushy, ax c"); + this.add(cardPreview, "cell 2 1, w 480:510:550, h 65%, growy, pushy, ax c"); + this.add(showOptionsButton, "cell 0 2, w 150!, h 26!, left, spanx 3, hidemode 3"); + this.add(optionsPanel, "cell 0 2, center, w 100%, spanx 3, hidemode 3"); + this.add(cmdPanel, "cell 0 3, w 100%, spanx 3"); - this.add(this.dateTimeCheck, "cell 0 2, w 50%, ax c"); - this.add(monthDropdown, "cell 0 3, w 10%, ax left, split 2, pad 0 2 0 0"); - this.add(yearDropdown, "cell 0 3, w 8%, ax left, split 2, pad 0 2 0 0"); - if (currentDeckIsNotEmpty) - this.add(this.createNewDeckCheckbox,"cell 2 2, ax left"); - this.add(this.cmdAccept, "cell 2 3, w 175, align r, h 26"); - this.add(this.cmdCancel, "cell 2 3, w 175, align r, h 26"); + // === ACTION LISTENERS + showOptionsButton.addActionListener(actionEvent -> { + optionsPanel.setVisible(true); + showOptionsButton.setVisible(false); + }); + hideOptionsButton.addActionListener(actionEvent -> { + optionsPanel.setVisible(false); + showOptionsButton.setVisible(true); + }); - this.cmdCancel.addActionListener(new ActionListener() { + this.cmdCancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { DeckImport.this.processWindowEvent(new WindowEvent(DeckImport.this, WindowEvent.WINDOW_CLOSING)); } }); - this.cmdAccept.addActionListener(new ActionListener() { + this.cmdAcceptButton.addActionListener(new ActionListener() { @SuppressWarnings("unchecked") @Override public void actionPerformed(final ActionEvent e) { @@ -306,6 +397,41 @@ public class DeckImport ex }; this.dateTimeCheck.addActionListener(updateDateCheck); + this.cardArtPrefsComboBox.addItemListener(new ItemListener() { + @Override public void itemStateChanged(final ItemEvent e) { + String artPreference = cardArtPrefsComboBox.getSelectedItem(); + if (artPreference == null) + artPreference = latestOpt; // default, just in case + final boolean latestArt = artPreference.equalsIgnoreCase(latestOpt); + final boolean coreExpFilter = StaticData.instance().isCoreExpansionOnlyFilterSet(); + controller.setCardArtPreference(latestArt, coreExpFilter); + parseAndDisplay(); + } + }); + + cardArtPrefFilter.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String artPreference = cardArtPrefsComboBox.getSelectedItem(); + if (artPreference == null) + artPreference = latestOpt; // default, just in case + final boolean latestArt = artPreference.equalsIgnoreCase(latestOpt); + final boolean coreExpFilter = cardArtPrefFilter.isSelected(); + controller.setCardArtPreference(latestArt, coreExpFilter); + parseAndDisplay(); + } + }); + + final ActionListener updateCardBlockCheck = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + final boolean isSel = blockCheck.isSelected(); + blocksDropdown.setEnabled(isSel); + parseAndDisplay(); + } + }; + this.blockCheck.addActionListener(updateCardBlockCheck); + final ActionListener reparse = new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { parseAndDisplay(); @@ -319,10 +445,11 @@ public class DeckImport ex this.yearDropdown.addActionListener(reparse); this.monthDropdown.addActionListener(reparse); + this.blocksDropdown.addActionListener(reparse); updateDateCheck.actionPerformed(null); // update actual state this.txtInput.getDocument().addDocumentListener(new OnChangeTextUpdate()); - this.cmdAccept.setEnabled(false); + this.cmdAcceptButton.setEnabled(false); if (currentDeckIsNotEmpty){ this.createNewDeckCheckbox.setSelected(false); @@ -332,52 +459,54 @@ public class DeckImport ex this.htmlOutput.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { - // TODO: FOIL and Card Status - if(e.getEventType() == HyperlinkEvent.EventType.ENTERED || - e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - String cardRef = e.getDescription(); - TokenKey tokenKey = DeckRecognizer.Token.parseTokenKey(cardRef); - StaticData data = StaticData.instance(); - PaperCard card = data.fetchCard(tokenKey.cardName, tokenKey.setCode, tokenKey.collectorNumber); - if (card != null){ - // no need to check for card that has Image because CardPicturePanel - // has automatic integration with cardFetch - String header; - if (tokenKey.deckSection != null) - header = String.format("%s: %s", - Localizer.getInstance().getMessage("lblDeckSection"), - tokenKey.deckSection); - else { - if (tokenKey.typeName.equals("illegal_card_request")) - header = String.format("%s %s", - Localizer.getInstance().getMessage("lblIllegalCardMsg"), - deckFormat.name()); - else - header = String.format("%s", - Localizer.getInstance().getMessage("lblInvalidCardMsg")); - } - CardEdition edition = data.getCardEdition(card.getEdition()); - String editionName = edition != null ? String.format("%s ", edition.getName()) : ""; - String cardLbl = String.format("%s, %s(%s), No. %s", tokenKey.cardName, - editionName, tokenKey.setCode, tokenKey.collectorNumber); - picturePreview.setItem(card); - if (!tokenKey.typeName.equals("legal_card_request")) - picturePreview.showAsDisabled(); - else - picturePreview.showAsEnabled(); - cardPreviewLabel.setText(String.format( - "%s
" + - "%s", - getTokenTypeColour(tokenKey.typeName), header, cardLbl)); - // set tooltip - picturePreview.setToolTipText(cardLbl); - - } - } + activateCardPreview(e); } }); } + private void activateCardPreview(HyperlinkEvent e) { + // TODO: FOIL and Card Status + if(e.getEventType() == HyperlinkEvent.EventType.ENTERED || + e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + String cardRef = e.getDescription(); + TokenKey tokenKey = DeckRecognizer.Token.parseTokenKey(cardRef); + StaticData data = StaticData.instance(); + PaperCard card = data.fetchCard(tokenKey.cardName, tokenKey.setCode, tokenKey.collectorNumber); + if (card != null){ + // no need to check for card that has Image because CardPicturePanel + // has automatic integration with cardFetch + String header; + if (tokenKey.deckSection != null) + header = String.format("%s: %s", + Localizer.getInstance().getMessage("lblDeckSection"), + tokenKey.deckSection); + else { + if (tokenKey.typeName.equals("illegal_card_request")) + header = String.format("%s", + Localizer.getInstance().getMessage("lblIllegalCardMsg", getGameFormatLabel())); + else + header = String.format("%s", + Localizer.getInstance().getMessage("lblInvalidCardMsg")); + } + CardEdition edition = data.getCardEdition(card.getEdition()); + String editionName = edition != null ? String.format("%s ", edition.getName()) : ""; + String cardLbl = String.format("%s, %s(%s), No. %s", tokenKey.cardName, + editionName, tokenKey.setCode, tokenKey.collectorNumber); + cardImagePreview.setItem(card); + if (!tokenKey.typeName.equals("legal_card_request")) + cardImagePreview.showAsDisabled(); + else + cardImagePreview.showAsEnabled(); + cardPreviewLabel.setText(String.format( + "%s
" + + "%s", + getTokenTypeColour(tokenKey.typeName), header, cardLbl)); + // set tooltip + cardImagePreview.setToolTipText(cardLbl); + } + } + } + private String getTokenTypeColour(String typeName){ switch (typeName.trim().toUpperCase()){ case "LEGAL_CARD_REQUEST": @@ -391,9 +520,9 @@ public class DeckImport ex } } - private void resetCardPreviewPanel() { + private void resetCardImagePreviewPanel() { this.cardPreviewLabel.setText(Localizer.getInstance().getMessage("lblCardPreview")); - this.picturePreview.setItem(ImageCache.getDefaultImage()); + this.cardImagePreview.setItem(ImageCache.getDefaultImage()); } /** @@ -429,13 +558,13 @@ public class DeckImport ex boolean createNewDeck = this.createNewDeckCheckbox.isSelected(); this.controller.setCreateNewDeck(createNewDeck); String cmdAcceptLabel = createNewDeck ? this.CREATE_NEW_DECK_CMD_LABEL : this.IMPORT_CARDS_CMD_LABEL; - this.cmdAccept.setText(cmdAcceptLabel); + this.cmdAcceptButton.setText(cmdAcceptLabel); } private void displayTokens(final List tokens) { if (tokens.isEmpty() || hasOnlyComment(tokens)) { htmlOutput.setText(HTML_WELCOME_TEXT); - resetCardPreviewPanel(); + resetCardImagePreviewPanel(); } else { final StringBuilder sbOut = new StringBuilder(""); sbOut.append(String.format("%s", DeckImport.STYLESHEET)); @@ -457,8 +586,8 @@ public class DeckImport ex private void updateSummaries(final List tokens) { CStatisticsImporter.instance().updateStats(tokens); - cmdAccept.setEnabled(CStatisticsImporter.instance().getTotalCardsInDecklist() > 0); - this.resetCardPreviewPanel(); + cmdAcceptButton.setEnabled(CStatisticsImporter.instance().getTotalCardsInDecklist() > 0); + this.resetCardImagePreviewPanel(); } private String toHTML(final DeckRecognizer.Token token) { @@ -469,24 +598,25 @@ public class DeckImport ex case LEGAL_CARD_REQUEST: PaperCard tokenCard = token.getCard(); return String.format("", + "(%s) %s %s", token.getKey(), token.getNumber(), tokenCard.getName(), tokenCard.getEdition(), - tokenCard.getCollectorNumber(), tokenCard.isFoil() ? "(FOIL)" : ""); + tokenCard.getCollectorNumber(), tokenCard.isFoil() ? " - FOIL -" : ""); case UNKNOWN_CARD_REQUEST: return String.format("
%s x %s (%s)
", token.getNumber(), token.getText(), Localizer.getInstance().getMessage("lblUnknownCardMsg")); case ILLEGAL_CARD_REQUEST: return String.format("", + "%s x %s %s (%s)", token.getKey(), token.getNumber(), token.getText(), - Localizer.getInstance().getMessage("lblIllegalCardMsg"), - this.deckFormat.name()); + token.getCard().isFoil() ? " - FOIL -" : "", + Localizer.getInstance().getMessage("lblIllegalCardMsg", getGameFormatLabel())); case INVALID_CARD_REQUEST: return String.format("", + "%s x %s %s (%s)", token.getKey(), token.getNumber(), token.getText(), + token.getCard().isFoil() ? " - FOIL -" : "", Localizer.getInstance().getMessage("lblInvalidCardMsg")); case DECK_SECTION_NAME: return String.format("
%s
", token.getText()); @@ -510,4 +640,12 @@ public class DeckImport ex return ""; } } + + private String getGameFormatLabel() { + if (this.blockCheck.isSelected() && this.blocksDropdown.getSelectedItem() != null) + return this.blocksDropdown.getSelectedItem().getName(); + else + return String.format("%s %s", + Localizer.getInstance().getMessage("lblFormat"), this.deckFormat.name()); + } }