Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import javax.swing.ButtonGroup;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import com.google.common.primitives.Ints;

Expand All @@ -16,6 +17,7 @@
import forge.model.FModel;
import forge.screens.match.CMatchUI;
import forge.screens.match.VAutoYields;
import forge.screens.match.views.VField;
import forge.screens.match.controllers.CDock.ArcState;
import forge.toolbox.FSkin.SkinIcon;
import forge.toolbox.FSkin.SkinnedCheckBoxMenuItem;
Expand Down Expand Up @@ -49,6 +51,8 @@ public JMenu getMenu() {
menu.addSeparator();
menu.add(getMenuItem_TargetingArcs());
menu.add(new CardOverlaysMenu(matchUI).getMenu());
menu.add(getSubmenu_StackGroupPermanents());
menu.add(getMenuItem_TokensSeparateRow());
menu.add(getMenuItem_AutoYields());
menu.addSeparator();
menu.add(getMenuItem_ViewDeckList());
Expand Down Expand Up @@ -204,4 +208,58 @@ private SkinnedMenuItem getMenuItem_ViewDeckList() {
private ActionListener getViewDeckListAction() {
return e -> matchUI.viewDeckList();
}

private SkinnedMenu getSubmenu_StackGroupPermanents() {
final Localizer localizer = Localizer.getInstance();
final SkinnedMenu submenu = new SkinnedMenu(localizer.getMessage("cbpStackGroupPermanents"));
final ButtonGroup group = new ButtonGroup();
final String current = prefs.getPref(FPref.UI_GROUP_PERMANENTS);

final String[] options = {"Default", "Stack Creatures", "Group Creatures/Tokens", "Group All Permanents"};
final String[] tooltips = {
"Creatures are never grouped or stacked. Identical lands and tokens are stacked (up to 5). Identical artifacts and enchantments are stacked (up to 4). Stacking fans cards out so each copy is partially visible.",
"Same as Default, but creatures are also stacked (up to 4).",
"Group identical creatures and tokens (5 or more) into a single compact pile with a count badge.",
"Group all identical permanents (5 or more) into a single compact pile with a count badge."
};
for (int i = 0; i < options.length; i++) {
SkinnedRadioButtonMenuItem item = new SkinnedRadioButtonMenuItem(options[i]);
item.setToolTipText(tooltips[i]);
item.setSelected(options[i].equals(current));
item.addActionListener(getGroupPermanentsAction(options[i]));
group.add(item);
submenu.add(item);
}
return submenu;
}

private SkinnedCheckBoxMenuItem getMenuItem_TokensSeparateRow() {
final Localizer localizer = Localizer.getInstance();
SkinnedCheckBoxMenuItem menuItem = new SkinnedCheckBoxMenuItem(localizer.getMessage("cbpTokensSeparateRow"));
menuItem.setToolTipText("Show tokens in their own row instead of mixed with creatures.");
menuItem.setState(prefs.getPrefBoolean(FPref.UI_TOKENS_IN_SEPARATE_ROW));
menuItem.addActionListener(e -> {
final boolean enabled = !prefs.getPrefBoolean(FPref.UI_TOKENS_IN_SEPARATE_ROW);
prefs.setPref(FPref.UI_TOKENS_IN_SEPARATE_ROW, enabled);
prefs.save();
SwingUtilities.invokeLater(() -> {
for (final VField f : matchUI.getFieldViews()) {
f.getTabletop().doLayout();
}
});
});
return menuItem;
}

private ActionListener getGroupPermanentsAction(final String value) {
return e -> {
prefs.setPref(FPref.UI_GROUP_PERMANENTS, value);
prefs.save();
SwingUtilities.invokeLater(() -> {
for (final VField f : matchUI.getFieldViews()) {
f.getTabletop().doLayout();
}
});
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public MouseTriggerEvent(final MouseEvent event) {
this.y = event.getY();
}

public MouseTriggerEvent(final int button, final int x, final int y) {
this.button = button;
this.x = x;
this.y = y;
}

@Override
public int getButton() {
return button;
Expand Down
52 changes: 52 additions & 0 deletions forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
private boolean isSelected;
private boolean hasFlash;
private CachedCardImage cachedImage;
private int groupCount;

private static Font smallCounterFont;
private static Font largeCounterFont;
Expand Down Expand Up @@ -250,6 +251,13 @@ public final void setDisplayEnabled(final boolean displayEnabled0) {
displayEnabled = displayEnabled0;
}

public int getGroupCount() {
return groupCount;
}
public void setGroupCount(int count) {
this.groupCount = count;
}

public final void setAnimationPanel(final boolean isAnimationPanel0) {
isAnimationPanel = isAnimationPanel0;
}
Expand Down Expand Up @@ -385,6 +393,9 @@ protected final void paintChildren(final Graphics g) {

}
displayIconOverlay(g, canShow);
if (groupCount >= 5) {
drawGroupCountBadge(g);
}
if (canShow) {
drawFoilEffect(g, card, cardXOffset, cardYOffset,
cardWidth, cardHeight, Math.round(cardWidth * BLACK_BORDER_SIZE));
Expand Down Expand Up @@ -496,6 +507,47 @@ private void displayCardNameOverlay(final boolean isVisible, final Dimension img
titleText.setVisible(isVisible);
}

private void drawGroupCountBadge(final Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

String text = "\u00D7" + groupCount;
Font badgeFont = new Font("Dialog", Font.BOLD, Math.max(10, cardWidth / 5));
FontMetrics fm = g2d.getFontMetrics(badgeFont);

int textWidth = fm.stringWidth(text);
int textHeight = fm.getAscent();
int padX = Math.max(4, cardWidth / 20);
int padY = Math.max(2, cardHeight / 30);
int badgeWidth = textWidth + padX * 2;
int badgeHeight = textHeight + padY * 2;
int badgeX = cardXOffset + 2;
int badgeY = cardYOffset + 2;

g2d.setColor(new Color(0, 0, 0, 180));
g2d.fillRoundRect(badgeX, badgeY, badgeWidth, badgeHeight, 6, 6);

g2d.setColor(Color.WHITE);
g2d.setFont(badgeFont);
g2d.drawString(text, badgeX + padX, badgeY + padY + textHeight);
}

public boolean isBadgeHit(int mouseX, int mouseY) {
if (groupCount < 5) {
return false;
}
// Mouse coordinates are container-relative (from PlayArea), so use
// getCardX()/getCardY() which convert panel-internal offsets to
// container coordinates (getX() + cardXOffset).
int badgeX = getCardX() + 2;
int badgeY = getCardY() + 2;
// Use generous hit area to cover the font-metrics-based badge size
int badgeWidth = Math.max(30, cardWidth / 3);
int badgeHeight = Math.max(20, cardHeight / 6);
return mouseX >= badgeX && mouseX <= badgeX + badgeWidth
&& mouseY >= badgeY && mouseY <= badgeY + badgeHeight;
}

private void displayIconOverlay(final Graphics g, final boolean canShow) {
if (canShow && showCardManaCostOverlay() && cardWidth < 200) {
final boolean showSplitMana = card.isSplitCard() && card.getZone() != ZoneType.Battlefield;
Expand Down
Loading