mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* UI: choose modes dialog improves:
* Added hotkeys to select options (1-9 for choice, SPACE/ENTER for done, ESC for cancel); * "Up to" modes choose dialog - added "done" button in dialog; * "Up to" modes choose dialog - fixed that user can't cancel if already selected one mode; * Added extra info about source object, selected and remaining modes to select, ability number for hotkey; * Fixed that mode choose dialog doesn't close on cancel (#6199);
This commit is contained in:
parent
cf97b9e6c7
commit
8add25fa12
6 changed files with 179 additions and 40 deletions
|
@ -1,7 +1,9 @@
|
|||
package mage.client.components.ability;
|
||||
|
||||
import mage.abilities.Modes;
|
||||
import mage.client.SessionHandler;
|
||||
import mage.client.dialog.MageDialog;
|
||||
import mage.client.game.GamePanel;
|
||||
import mage.client.util.ImageHelper;
|
||||
import mage.remote.Session;
|
||||
import mage.view.AbilityPickerView;
|
||||
|
@ -22,7 +24,7 @@ import java.util.*;
|
|||
/**
|
||||
* Dialog for choosing abilities.
|
||||
*
|
||||
* @author nantuko
|
||||
* @author nantuko, JayDi85
|
||||
*/
|
||||
public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
||||
|
||||
|
@ -41,7 +43,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
|
||||
private BackgroundPainter mwPanelPainter;
|
||||
private JScrollPane jScrollPane2;
|
||||
private JTextField title;
|
||||
private JLabel title;
|
||||
|
||||
private Image rightImage;
|
||||
private Image rightImageHovered;
|
||||
|
@ -68,9 +70,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
public AbilityPicker(List<Object> choices, String message) {
|
||||
this.choices = choices;
|
||||
setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
|
||||
if (message != null) {
|
||||
this.message = message + " (single-click)";
|
||||
}
|
||||
setMessageAndPrepare(message);
|
||||
initComponents();
|
||||
jScrollPane2.setOpaque(false);
|
||||
jScrollPane2.getViewport().setOpaque(false);
|
||||
|
@ -80,7 +80,6 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
}
|
||||
|
||||
public void init(UUID gameId) {
|
||||
|
||||
this.gameId = gameId;
|
||||
}
|
||||
|
||||
|
@ -93,11 +92,17 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
public void show(AbilityPickerView choices, Point p) {
|
||||
this.choices = new ArrayList<>();
|
||||
this.selected = true; // to stop previous modal
|
||||
setMessageAndPrepare(choices.getMessage());
|
||||
|
||||
// if not cancel from server then add own
|
||||
boolean wasCancelButton = false;
|
||||
for (Map.Entry<UUID, String> choice : choices.getChoices().entrySet()) {
|
||||
wasCancelButton = wasCancelButton || choice.getKey().equals(Modes.CHOOSE_OPTION_CANCEL_ID);
|
||||
this.choices.add(new AbilityPickerAction(choice.getKey(), choice.getValue()));
|
||||
}
|
||||
this.choices.add(new AbilityPickerAction(null, "Cancel"));
|
||||
if (!wasCancelButton) {
|
||||
this.choices.add(new AbilityPickerAction(null, "Cancel"));
|
||||
}
|
||||
|
||||
show(this.choices);
|
||||
}
|
||||
|
@ -109,6 +114,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
rows.setListData(this.choices.toArray());
|
||||
this.rows.setSelectedIndex(0);
|
||||
this.selected = false; // back to false - waiting for selection
|
||||
this.title.setText(this.message);
|
||||
setVisible(true);
|
||||
|
||||
MageDialog.makeWindowCentered(this, DIALOG_WIDTH, DIALOG_HEIGHT);
|
||||
|
@ -116,30 +122,17 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
}
|
||||
|
||||
private void initComponents() {
|
||||
JLabel jLabel1;
|
||||
JLabel jLabel3;
|
||||
|
||||
Color textColor = Color.white;
|
||||
|
||||
mwPanelPainter = new BackgroundPainter();
|
||||
jLabel1 = new JLabel();
|
||||
jLabel3 = new JLabel();
|
||||
|
||||
title = new JTextField();
|
||||
jScrollPane2 = new JScrollPane();
|
||||
|
||||
setBackground(textColor);
|
||||
setBackgroundPainter(mwPanelPainter);
|
||||
jLabel1.setFont(new Font("Times New Roman", 1, 18));
|
||||
jLabel1.setForeground(textColor);
|
||||
jLabel1.setText(message);
|
||||
|
||||
jLabel3.setForeground(textColor);
|
||||
jLabel3.setHorizontalAlignment(SwingConstants.TRAILING);
|
||||
jLabel3.setText("Selected:");
|
||||
|
||||
title.setFont(new Font("Tahoma", 1, 11));
|
||||
title.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||
title = new JLabel();
|
||||
title.setFont(new Font("Times New Roman", 1, 15));
|
||||
title.setForeground(textColor);
|
||||
title.setText(message);
|
||||
|
||||
jScrollPane2.setBorder(null);
|
||||
jScrollPane2.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
@ -161,6 +154,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
rows.setMinimumSize(new Dimension(67, 16));
|
||||
rows.setOpaque(false);
|
||||
|
||||
// mouse actions
|
||||
rows.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent evt) {
|
||||
|
@ -174,6 +168,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
rows.setBorder(BorderFactory.createEmptyBorder());
|
||||
rows.addMouseWheelListener(this);
|
||||
|
||||
|
||||
jScrollPane2.setViewportView(rows);
|
||||
jScrollPane2.setViewportBorder(BorderFactory.createEmptyBorder());
|
||||
|
||||
|
@ -184,7 +179,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
GroupLayout.TRAILING,
|
||||
layout.createSequentialGroup().addContainerGap().add(
|
||||
layout.createParallelGroup(GroupLayout.TRAILING).add(GroupLayout.LEADING, jScrollPane2, GroupLayout.DEFAULT_SIZE, 422, Short.MAX_VALUE).add(GroupLayout.LEADING,
|
||||
layout.createSequentialGroup().add(jLabel1).addPreferredGap(LayoutStyle.RELATED, 175, Short.MAX_VALUE).add(1, 1, 1)).add(
|
||||
layout.createSequentialGroup().add(title).addPreferredGap(LayoutStyle.RELATED, 175, Short.MAX_VALUE).add(1, 1, 1)).add(
|
||||
GroupLayout.LEADING,
|
||||
layout.createSequentialGroup().add(layout.createParallelGroup(GroupLayout.LEADING)
|
||||
)
|
||||
|
@ -197,7 +192,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.LEADING).add(
|
||||
layout.createSequentialGroup().add(
|
||||
layout.createParallelGroup(GroupLayout.LEADING).add(
|
||||
layout.createSequentialGroup().add(jLabel1, GroupLayout.PREFERRED_SIZE, 36, GroupLayout.PREFERRED_SIZE)
|
||||
layout.createSequentialGroup().add(title, GroupLayout.PREFERRED_SIZE, 72, GroupLayout.PREFERRED_SIZE)
|
||||
.add(5, 5, 5)
|
||||
.add(
|
||||
layout.createParallelGroup(GroupLayout.BASELINE)
|
||||
|
@ -463,4 +458,102 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
|
|||
log.error("Couldn't cancel choose dialog: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setMessageAndPrepare(String message) {
|
||||
if (message != null) {
|
||||
this.message = message + " (single-click or hotkeys)";
|
||||
} else {
|
||||
this.message = DEFAULT_MESSAGE;
|
||||
}
|
||||
this.message = "<html>" + this.message;
|
||||
}
|
||||
|
||||
private void tryChoiceDone() {
|
||||
// done by keyboard
|
||||
if (!isVisible() || choices == null) {
|
||||
return;
|
||||
}
|
||||
for (Object obj : choices) {
|
||||
AbilityPickerAction action = (AbilityPickerAction) obj;
|
||||
if (Modes.CHOOSE_OPTION_DONE_ID.equals(action.id)) {
|
||||
action.actionPerformed(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryChoiceCancel() {
|
||||
// cancel by keyboard
|
||||
if (!isVisible() || choices == null) {
|
||||
return;
|
||||
}
|
||||
for (Object obj : choices) {
|
||||
AbilityPickerAction action = (AbilityPickerAction) obj;
|
||||
if (Modes.CHOOSE_OPTION_DONE_ID.equals(action.id)) {
|
||||
action.actionPerformed(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryChoiceOption(int choiceNumber) {
|
||||
// choice by keyboard
|
||||
if (!isVisible() || choices == null) {
|
||||
return;
|
||||
}
|
||||
String need = choiceNumber + ".";
|
||||
for (Object obj : choices) {
|
||||
AbilityPickerAction action = (AbilityPickerAction) obj;
|
||||
if (action.toString().startsWith(need)) {
|
||||
action.actionPerformed(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void injectHotkeys(GamePanel panel, String commandsPrefix) {
|
||||
// TODO: fix that GamePanel recive imput from any place, not only active (e.g. F9 works from lobby)
|
||||
int c = JComponent.WHEN_IN_FOCUSED_WINDOW;
|
||||
|
||||
// choice keys
|
||||
Map<Integer, Integer> numbers = new HashMap<>();
|
||||
numbers.put(KeyEvent.VK_1, 1);
|
||||
numbers.put(KeyEvent.VK_2, 2);
|
||||
numbers.put(KeyEvent.VK_3, 3);
|
||||
numbers.put(KeyEvent.VK_4, 4);
|
||||
numbers.put(KeyEvent.VK_5, 5);
|
||||
numbers.put(KeyEvent.VK_6, 6);
|
||||
numbers.put(KeyEvent.VK_7, 7);
|
||||
numbers.put(KeyEvent.VK_8, 8);
|
||||
numbers.put(KeyEvent.VK_9, 9);
|
||||
numbers.forEach((vk, num) -> {
|
||||
KeyStroke ks = KeyStroke.getKeyStroke(vk, 0);
|
||||
panel.getInputMap(c).put(ks, commandsPrefix + "_CHOOSE_" + num);
|
||||
panel.getActionMap().put(commandsPrefix + "_CHOOSE_" + num, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
tryChoiceOption(num);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// done key (space, enter)
|
||||
panel.getInputMap(c).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), commandsPrefix + "_CHOOSE_DONE");
|
||||
panel.getInputMap(c).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), commandsPrefix + "_CHOOSE_DONE");
|
||||
panel.getActionMap().put(commandsPrefix + "_CHOOSE_DONE", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
tryChoiceDone();
|
||||
}
|
||||
});
|
||||
|
||||
// cancel key (esc)
|
||||
panel.getInputMap(c).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), commandsPrefix + "_CHOOSE_CANCEL");
|
||||
panel.getActionMap().put(commandsPrefix + "_CHOOSE_CANCEL", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
tryChoiceCancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,7 +394,6 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
this.feedbackPanel.init(gameId);
|
||||
this.feedbackPanel.clear();
|
||||
this.abilityPicker.init(gameId);
|
||||
|
||||
this.btnConcede.setVisible(true);
|
||||
this.btnStopWatching.setVisible(false);
|
||||
this.btnSwitchHands.setVisible(false);
|
||||
|
@ -1399,6 +1398,8 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
public void select(String message, GameView gameView, int messageId, Map<String, Serializable> options) {
|
||||
this.abilityPicker.setVisible(false);
|
||||
|
||||
holdingPriority = false;
|
||||
txtHoldPriority.setVisible(false);
|
||||
setMenuStates(
|
||||
|
@ -1464,6 +1465,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
private void hideAll() {
|
||||
this.abilityPicker.setVisible(false);
|
||||
ActionCallback callback = Plugins.instance.getActionCallback();
|
||||
((MageActionCallback) callback).hideGameUpdate(gameId);
|
||||
}
|
||||
|
@ -1900,6 +1902,9 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
});
|
||||
|
||||
// special hotkeys for custom rendered dialogs without focus
|
||||
this.abilityPicker.injectHotkeys(this, "ABILITY_PICKER");
|
||||
|
||||
final BasicSplitPaneUI myUi = (BasicSplitPaneUI) jSplitPane0.getUI();
|
||||
final BasicSplitPaneDivider divider = myUi.getDivider();
|
||||
final JButton upArrowButton = (JButton) divider.getComponent(0);
|
||||
|
@ -2268,6 +2273,10 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
for (ComponentListener cl : this.getComponentListeners()) {
|
||||
this.removeComponentListener(cl);
|
||||
}
|
||||
|
||||
for (KeyListener kl : this.getKeyListeners()) {
|
||||
this.removeKeyListener(kl);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnConcedeActionPerformed(java.awt.event.ActionEvent evt) {
|
||||
|
@ -2711,5 +2720,4 @@ class ReplayTask extends SwingWorker<Void, Collection<MatchView>> {
|
|||
} catch (CancellationException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
package mage.view;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class AbilityPickerView implements Serializable {
|
||||
|
@ -17,6 +16,7 @@ public class AbilityPickerView implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Map<UUID, String> choices = new LinkedHashMap<>();
|
||||
private String message = null;
|
||||
|
||||
public AbilityPickerView(String objectName, List<? extends Ability> abilities) {
|
||||
for (Ability ability : abilities) {
|
||||
|
@ -32,11 +32,16 @@ public class AbilityPickerView implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public AbilityPickerView(Map<UUID, String> modes) {
|
||||
public AbilityPickerView(Map<UUID, String> modes, String message) {
|
||||
this.choices = modes;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Map<UUID, String> getChoices() {
|
||||
return choices;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2040,10 +2040,14 @@ public class HumanPlayer extends PlayerImpl {
|
|||
}
|
||||
|
||||
if (modes.size() > 1) {
|
||||
// done option for up to choices
|
||||
boolean canEndChoice = modes.getSelectedModes().size() >= modes.getMinModes();
|
||||
MageObject obj = game.getObject(source.getSourceId());
|
||||
Map<UUID, String> modeMap = new LinkedHashMap<>();
|
||||
int modeIndex = 0;
|
||||
AvailableModes:
|
||||
for (Mode mode : modes.getAvailableModes(source, game)) {
|
||||
modeIndex++;
|
||||
int timesSelected = modes.getSelectedStats(mode.getId());
|
||||
for (UUID selectedModeId : modes.getSelectedModes()) {
|
||||
Mode selectedMode = modes.get(selectedModeId);
|
||||
|
@ -2068,18 +2072,31 @@ public class HumanPlayer extends PlayerImpl {
|
|||
modeText = "(selected " + timesSelected + "x) " + modeText;
|
||||
}
|
||||
}
|
||||
modeMap.put(mode.getId(), modeText);
|
||||
modeMap.put(mode.getId(), modeIndex + ". " + modeText);
|
||||
}
|
||||
}
|
||||
|
||||
if (!modeMap.isEmpty()) {
|
||||
|
||||
// can done for up to
|
||||
if (canEndChoice) {
|
||||
modeMap.put(Modes.CHOOSE_OPTION_DONE_ID, "Done");
|
||||
}
|
||||
modeMap.put(Modes.CHOOSE_OPTION_CANCEL_ID, "Cancel");
|
||||
|
||||
boolean done = false;
|
||||
while (!done && canRespond()) {
|
||||
|
||||
String message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes()
|
||||
+ ", min " + modes.getMinModes() + ")";
|
||||
if (obj != null) {
|
||||
message = message + "<br>" + obj.getLogName();
|
||||
}
|
||||
|
||||
updateGameStatePriority("chooseMode", game);
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
game.fireGetModeEvent(playerId, "Choose Mode", modeMap);
|
||||
game.fireGetModeEvent(playerId, message, modeMap);
|
||||
}
|
||||
waitForResponse(game);
|
||||
|
||||
|
@ -2091,9 +2108,21 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return mode;
|
||||
}
|
||||
}
|
||||
} else if (modes.getSelectedModes().size() >= modes.getMinModes()) {
|
||||
/* let the player cancel mode selection if they do not need to select any further modes */
|
||||
done = true;
|
||||
|
||||
// end choice by done option in ability pickup dialog
|
||||
if (canEndChoice && Modes.CHOOSE_OPTION_DONE_ID.equals(response.getUUID())) {
|
||||
done = true;
|
||||
}
|
||||
|
||||
// cancel choice (remove all selections)
|
||||
if (Modes.CHOOSE_OPTION_CANCEL_ID.equals(response.getUUID())) {
|
||||
modes.getSelectedModes().clear();
|
||||
return null;
|
||||
}
|
||||
} else if (canEndChoice) {
|
||||
// end choice by done button in feedback panel
|
||||
// disable after done option implemented
|
||||
// done = true;
|
||||
}
|
||||
if (source.getAbilityType() != AbilityType.TRIGGERED) {
|
||||
done = true;
|
||||
|
|
|
@ -207,7 +207,7 @@ public class GameController implements GameCallback {
|
|||
choosePile(event.getPlayerId(), event.getMessage(), event.getPile1(), event.getPile2());
|
||||
break;
|
||||
case CHOOSE_MODE:
|
||||
chooseMode(event.getPlayerId(), event.getModes());
|
||||
chooseMode(event.getPlayerId(), event.getModes(), event.getMessage());
|
||||
break;
|
||||
case CHOOSE_CHOICE:
|
||||
chooseChoice(event.getPlayerId(), event.getChoice());
|
||||
|
@ -769,8 +769,8 @@ public class GameController implements GameCallback {
|
|||
perform(playerId, playerId1 -> getGameSession(playerId1).choosePile(message, new CardsView(pile1), new CardsView(pile2)));
|
||||
}
|
||||
|
||||
private synchronized void chooseMode(UUID playerId, final Map<UUID, String> modes) throws MageException {
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(modes)));
|
||||
private synchronized void chooseMode(UUID playerId, final Map<UUID, String> modes, final String message) throws MageException {
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(modes, message)));
|
||||
}
|
||||
|
||||
private synchronized void chooseChoice(UUID playerId, final Choice choice) throws MageException {
|
||||
|
|
|
@ -18,6 +18,10 @@ import java.util.*;
|
|||
*/
|
||||
public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||
|
||||
// choose ID for options in ability/mode picker dialogs
|
||||
public static final UUID CHOOSE_OPTION_DONE_ID = UUID.fromString("33e72ad6-17ae-4bfb-a097-6e7aa06b49e9");
|
||||
public static final UUID CHOOSE_OPTION_CANCEL_ID = UUID.fromString("0125bd0c-5610-4eba-bc80-fc6d0a7b9de6");
|
||||
|
||||
private Mode currentMode; // the current mode of the selected modes
|
||||
private final List<UUID> selectedModes = new ArrayList<>(); // all selected modes (this + duplicate)
|
||||
private final Map<UUID, Mode> duplicateModes = new LinkedHashMap<>(); // for 2x selects: copy mode and put it to duplicate list
|
||||
|
|
Loading…
Reference in a new issue