Java swt change typed character - java

I have swt browser widget in which user can type with keyboard, I need for certain character user press change it to others.
for example when user press x, I change it y.
I add key listener where I can block user input with doit = false;
but now I can't pass my character.
here is what I am doing:
browser_1.addListener(SWT.KeyDown, new Listener() {
public void handleEvent(Event arg0) {
if(arg0.character=='x')
{
arg0.doit=false;
//now here how to send y as a charachter to browser widget
}
}
});
In other words can I somehow change character to other without using arg0.doit=false;

So after some search, here is the solution
In SWT you can add to display 'filter' listener instance which can modify pretty anything in the event (see docs for details).
Caution from Javadoc: Setting the type of an event to SWT.None from within the handleEvent() method can be used to change the event type and stop subsequent Java listeners from running. Because event filters run before other listeners, event filters can both block other listeners and set arbitrary fields within an event. For this reason, event filters are both powerful and dangerous. They should generally be avoided for performance, debugging and code maintenance reasons.
Here's the code (changes any typed key to 'l' character and wrote that in console, when the event actually arise)
browser.addListener(SWT.KeyDown, new Listener() {
public void handleEvent(Event event) {
System.out.println(event.character);
}
});
display.addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event event) {
if(event.widget instanceof Browser) {
event.character = 'l';
}
}
});
IMHO it's really dirty solution, implementation on browser side (by JavaScript) is much more prettier
Also when I'm looking to your code (don't know if it's just some testing, proof-of-concept code, anyway), using variables with something_number or arg0 makes me sad. It makes code so much unreadable and obscure, try to avoid them ;]..

You can do following:
Text textControl = new Text(...);
textControl.addKeyListener(this);
...
public void keyPressed(KeyEvent e) {
if (e.character == 'x' && (e.stateMask & SWT.CONTROL) == 0) {
e.doit = false;
textControl.insert("y");
}
}
Some comments to this code:
We check for e.stateMask because we still need keep CTRL+X as cut-function. Please note, if you use instead of current code this one (that just check that no special button is pressed):
if (e.character == 'x' && e.stateMask == 0)
You will get a bug when CapsLock is on. In this case user should press Shift+X to get lower x.
Method insert("y") inserts charater to the place of cursor. When some text is seleted, the whole selection will be replaced by "y".
Current example changes only lower case of "x". You should change it if needed to handle also change upper X to Y.

I had a similar requirement: converting a decimal separator press on the keypad (a dot) to the decimal separator of our locale (a comma). I tried the same idea as Sorceror, but it didn't work for me either. What does work is setting event.doit = false and posting a new event that is the clone of the original event with the character replaced:
#Override
public void handleEvent(Event event) {
if (event.widget instanceof Browser && event.character == 'x') {
Event eventClone = cloneEvent(event);
eventClone.character = 'y';
event.doit = false;
display.post(eventClone);
}
}
(If display is a local variable you need to make it final.)
I created a small utility method to create a clone of the event:
/**
* #return a clone of the given {#link Event}
*/
public static Event cloneEvent(Event event) {
Event clone = new Event();
clone.display = event.display;
clone.widget = event.widget;
clone.type = event.type;
clone.detail = event.detail;
clone.item = event.item;
clone.index = event.index;
clone.gc = event.gc;
clone.x = event.x;
clone.y = event.y;
clone.width = event.width;
clone.height = event.height;
clone.count = event.count;
clone.time = event.time;
clone.button = event.button;
clone.character = event.character;
clone.keyCode = event.keyCode;
clone.keyLocation = event.keyLocation;
clone.stateMask = event.stateMask;
clone.start = event.start;
clone.end = event.end;
clone.text = event.text;
clone.doit = event.doit;
clone.data = event.data;
clone.touches = event.touches;
clone.xDirection = event.xDirection;
clone.yDirection = event.yDirection;
clone.magnification = event.magnification;
clone.rotation = event.rotation;
return clone;
}

Related

How should I make my getMove() method interact with my GUI in my chess application?

I'm developing an application in Java to help me land my first job as a junior developer. It's a chess game with a GUI that both human players click on from the same machine.
When it's, say, white's turn to move, the application calls white's getMove(Interface interaction) method until a valid MoveAttempt is returned. Here's the getMove(Interface interaction) method of HumanPlayer:
public MoveAttempt getMove(Interface interaction) {
while(!interaction.selectionMade()) {
}
byte pieceFile = interaction.getPenultimateFile();
byte pieceRank = interaction.getPenultimateRank();
byte toFile = interaction.getUltimateFile();
byte toRank = interaction.getUltimateRank();
return new MoveAttempt(pieceFile, pieceRank, toFile, toRank, getIsWhite());
}
penultimateFile, penultimateRank, ultimateFile and ultimateRank are supposed to store the file (column) and rank (row) of the last two chess tiles clicked. This is achieved through this actionPerformed(ActionEvent event) which Interface has because it implements ActionListener
public void actionPerformed(ActionEvent event) {
LocalizedButton button = (LocalizedButton) event.getSource();
if(penultimateFile == -1) {
penultimateFile = button.getFile();
penultimateRank = button.getRank();
}
else {
ultimateFile = button.getFile();
ultimateRank = button.getRank();
}
}
and by calling this method before each call to getMove(Interface interaction)
public void resetClicks() {
penultimateFile = -1;
penultimateRank = -1;
ultimateFile = -1;
ultimateRank = -1;
}
So the idea is that a move attempt is not made until someone has clicked on two chess squares which is why I have a while loop indefinitely calling selectionMade():
public boolean selectionMade() {
return penultimateFile != -1 && penultimateRank != -1 && ultimateFile != -1 && ultimateRank != -1;
}
This didn't work---pieces didn't move---so in an attempt to see what was happening I put this print statement
System.out.println(interaction.getPenultimateFile() + ", " +
interaction.getPenultimateRank() + ", " +
interaction.getUltimateFile() + ", " +
interaction.getUltimateRank());
into the while loop to see what was going on and now it works---pieces move---except I may have encountered times in which it didn't work but I last I tried I couldn't get it to fail.
I don't want to print anything to the console; what should I do in lieu of having this while loop?
Edit: Putting boolean lol = 0 just above the loop and lol = !lol in the loop doesn't allow the code to work. Neither does calling doNothing().
Edit: Here's the source code: https://github.com/JosephBGriffith/Chess
Right now only the pawns work because I have other bugs that I need to fix. En passant works except the opponent piece doesn't get eliminated.
I would invert the control, so that the UI pushes moves to the game, rather than the game trying to pull moves from the UI.
So your game class might have:
class Game {
boolean move(int fromFile, int fromRank, int toFile, int toRank) { ... }
...
}
If the move wasn't legal (e.g. if it was the other player's turn) then move returns false and the move doesn't occur. That is, the internal state of the Game is unchanged.
And your actionPerformed method becomes:
public void actionPerformed(ActionEvent event) {
LocalizedButton button = (LocalizedButton) event.getSource();
if(penultimateFile == -1) {
penultimateFile = button.getFile();
penultimateRank = button.getRank();
}
else {
game.move(penultimateFile, penultimateRank, button.getFile(), button.getRank());
penultimateFile = -1;
}
}
You could use the return value of move to provide some feedback to the user if the move is illegal.
Something to note about this suggestion is that move is executed on the Swing event thread. In theory this is bad practice, although unless your move method is very slow it won't matter.
Read https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html and consider whether you want to use invokeLater.

How to wait on a button for user input in order to set a variable

I am writing a class that has certain variables that don't need to be set except in certain fringe cases. As such, I am working on writing a requestVariable() method that asks for user input to set that variable when it is needed. However, I am struggling to figure out how to wait for that input before moving on. Let me show you what I have.
SkillApplication AR_D_Atk_Def_1_app = (Unit) -> {
if (Unit.getAttackStatus() != com.codecademy.myapplication.Unit.AttackStatus.DEFENDER) {
return;
}
else {
// Make sure structuresAR is set
Unit.setStructuresAR(requestVariable( /* some parameters */ );
int X;
if (Unit.getStructuresAR() >= 5) {
X = 4;
}
else if (Unit.getStructuresAR() == 4) {
X = 3;
}
else if (Unit.getStructuresAR() == 3) {
X = 2;
}
else {
X = 1;
}
Unit.addCombatAtk(X);
Unit.addCombatDef(X);
}
};
This is a lambda function for a certain skill's application. If this skill needs to be applied, it will run this lambda function. This is one of the fringe cases where the member "structuresAR" of Unit needs to be used. It's very rarely used, and rather than having the user set it every time, I have it set in this lambda function.
VariableRequest<Integer> requestInteger = (VariableRequest<Integer>) (questionMessage, choices, layout) -> {
final Integer[] retVal = new Integer[1];
TextView questionView = new TextView(layout.getContext());
questionView.setText(questionMessage);
EditText textEntry = new EditText(layout.getContext());
textEntry.setInputType(InputType.TYPE_CLASS_NUMBER);
Button submitButton = new Button(layout.getContext());
submitButton.setText("Submit");
layout.addView(questionView);
layout.addView(textEntry);
layout.addView(submitButton);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retVal[0] = Integer.parseInt(String.valueOf(textEntry.getText()));
}
});
};
Here's what I have written so far for that function. It sets a question, options, and submit button to a layout that updates a return value with what is in the entry box when a button is clicked.
This problem is, this just keeps going. The rest of whatever I've written will be run while the onClickListener is still there, and I don't know how to wait until that's been clicked. I'm coming from C++ knowledge where just writing cin >> variable would pause and wait for you to enter. I'm trying to replicate that with a button.
There's also other problems I can spot with this such as getting the layout from inside a static method, but I struggle to come up with another method as I'm very new to Android development.

How can I get JavaFx Combobox to respond to user typing?

I'm writing a program which involves having a user type in a combo box and the list of items should change to reflect the text in the box (similar to autocomplete when you type into Google)
However, my combo box will not update until I press Enter. It doesn't seem to update when regular keys are typed. I have tried adding all kinds of listeners to the combo box but none of them fix the problem.
Here is the code snippet that has been the most successful. It is called from the fxml code: onKeyReleased="#keyReleased". It works properly, but still only executes when Enter is pressed.
public void keyReleased() throws SQLException, ClassNotFoundException
{
String coname = custconame_combo.getValue();
scriptHandler = new ScriptHandler();
custconame_combo.getItems().clear();
int i = 0;
for (String s : scriptHandler.searchCustomer(coname))
{
System.out.println(s);
custconame_combo.getItems().add(s);
custconame_combo.show();
i += 1;
}
}
I have searched high and low and still can't seem to solve this issue.
Since I've solved my problem, I'll share what I found.
Third party libraries provided the easiest solution. I went with the autocompletion class from JFoenix. It has exactly the functionality I was looking for and didn't feel like I was trying to reinvent the wheel.
This answer was very helpful in my search: JavaFX TextField Auto-suggestions
Just had a similiar porblem. The onKeyReleased method doesn't respond as needed. Use the EventHandler.
Here is my code (just tested and works well):
currencySymbolComboBox.setOnKeyPressed(event -> {
if(currencySymbolComboBox.isShowing()) {
if(event.getCode().isLetterKey()) {
currencyComboBoxKeysTyped += event.getCode().getName();
Optional<String> os = currecnySymbolsObservableList.stream()
.filter(symbol -> symbol.startsWith(currencyComboBoxKeysTyped))
.findFirst();
if (os.isPresent()) {
int ind = currecnySymbolsObservableList.indexOf(os.get());
ListView<String> lv = ((ComboBoxListViewSkin) currencySymbolComboBox.getSkin()).getListView();
lv.getFocusModel().focus(ind);
lv.scrollTo(ind);
currencySymbolComboBox.getSelectionModel().select(ind);
} else {
currencyComboBoxKeysTyped = currencyComboBoxKeysTyped
.substring(0, currencyComboBoxKeysTyped.length() - 1);
}
}
else if(event.getCode() == KeyCode.BACK_SPACE) {
if(currencyComboBoxKeysTyped.length() > 0) {
currencyComboBoxKeysTyped = currencyComboBoxKeysTyped
.substring(0, currencyComboBoxKeysTyped.length() - 1);
}
}
}
});
currencySymbolComboBox.showingProperty().addListener((observable, oldValue, newValue) -> {
if(!currencySymbolComboBox.isShowing()) {
currencyComboBoxKeysTyped = "";
}
});

Java showInputDialog select custom text

I have rename dialog for rename file
String renameTo = JOptionPane.showInputDialog(gui, "New Name", currentFile.getName());
it works this way, but I have a problem.
the problem is that I set the default value with the extension of the file
but I just want the file name to be selected.
sample : my file name = yusuf.png
I want select only yusuf like;
There is a lot going on inside JOptionPane, it's one of the things that makes it so powerful, it also makes it a little inflexible to.
Two immediate problems are apparent...
You can't gain direct access to the JTextField been used to get input from the user
The JOptionPane wants to control which components have focus when the dialog is first shown.
Setting up the JTextField is actually straight forward...
String text = "yusuf.png";
int endIndex = text.lastIndexOf(".");
JTextField field = new JTextField(text, 20);
if (endIndex > 0) {
field.setSelectionStart(0);
field.setSelectionEnd(endIndex);
} else {
field.selectAll();
}
This will basically select all the text from the start of the String up to the last . or all the text if no . can be found.
The difficult part now is taking back focus control from the JOptionPane
// Make a basic JOptionPane instance
JOptionPane pane = new JOptionPane(field,
JOptionPane.PLAIN_MESSAGE,
JOptionPane.OK_CANCEL_OPTION,
null);
// Use it's own dialog creation process, it's simpler this way
JDialog dialog = pane.createDialog("Rename");
// When the window is displayed, we want to "steal"
// focus from what the `JOptionPane` has set
// and apply it to our text field
dialog.addWindowListener(new WindowAdapter() {
#Override
public void windowActivated(WindowEvent e) {
// Set a small "delayed" action
// to occur at some point in the future...
// This way we can circumvent the JOptionPane's
// focus control
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
field.requestFocusInWindow();
}
});
}
});
// Put it on the screen...
dialog.setVisible(true);
dialog.dispose();
// Get the resulting action (what button was activated)
Object value = pane.getValue();
if (value instanceof Integer) {
int result = (int)value;
// OK was actioned, get the new name
if (result == JOptionPane.OK_OPTION) {
String newName = field.getText();
System.out.println("newName = " + newName);
}
}
And, crossing our fingers, we end up with something looking like...
Personally, I'd wrap this up in a nice reusable class/method call which returned the new text or null based on the action of the user, but that's me
Isn't there an easier way?
Of course, I just like showing you the most difficult solution possible ... 😳 (sarcasm) ... it's kind of why I suggested wrapping it up in it's own utility class, so you can re-use it later 😉

JavaFX TextArea: how to set tabulation width

How do I set tab width of JavaFX TextArea ?
When I use tabulation (tab key) in TextArea, the width of the tabulation is wide. I want to control the width, i.e., use 4 spaces. In the documentation I could not find a method to do this.
I tried this code (where taInput is a TextArea), but it is not working as it should:
taInput.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.TAB) {
// TAB SPACES
StringBuilder sb = new StringBuilder(config.getTabSpacesCount());
for (int i=0; i<config.getTabSpacesCount(); i++) {
sb.append(' ');
}
taInput.insertText(taInput.getCaretPosition(), sb.toString());
e.consume();
}
}
});
Finally I found a way to do this.
It seems that the setOnKeyPressed() method is not good for this task because the event is handled after the keyPress action is executed.
The addEventFilter() handles the events before their actions are executed, so you can manipulate the events.
My new code:
taInput.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.TAB) {
String s = StringUtils.repeat(' ', config.getTabSpacesCount());
taInput.insertText(taInput.getCaretPosition(), s);
e.consume();
}
}
});
#tenotron
your code also executes same logic for combination of TAB key with set of modifiers ( shift, control, alt, meta or shortcut). Meaning
In TextArea
Pressing TAB key = Ctrl(modifier) + TAB = .... = your logic.
To fix this issue , you have to use KeyCombination
Sample Code :
textArea.addEventFilter(KeyEvent.KEY_PRESSED,
new EventHandler<KeyEvent>() {
final KeyCombination combo = new KeyCodeCombination(
KeyCode.TAB);
#Override
public void handle(KeyEvent event) {
// check for only tab key
if (combo.match(event)) {
textArea.insertText(textArea.getCaretPosition(),
"I am not real TAB");
event.consume();
}
}
});
now Pressing TAB key results "I am not Real TAB" , ctrl+TAB will highlight the next Node on the scene.
Reference :
Correctly Checking KeyEvents
KeyCombination
From JavaFX 14 onward, the best way to deal with this is to use CSS to change the tab width, as shown in my answer to Setting the tab spacing/size visualization for a JavaFX TextArea
Replacing tab characters with multiple spaces doesn't have the same effect as tabs advance to the next tab stop, they don't add a fixed-width gap. Even if you adjusted for the characters preceding the tab, when not using a fixed-width font, an integer number of actual spaces may not give you the correct position.
Try making what you want displayed as a String. Then use s.replace("\t", " ");
if you want four spaces. This worked for me.

Categories

Resources