Reputation: 19
I want to use unicode characters to visualise playing cards for a poker game. I print them in JTextPane on seperate lines. As you can see on the first image they tend to overflow to other lines sometimes. I am outputing the cards using the same method, so it is really weird that it happens only sometimes. It's totally random.
Every card has the same style set through SimpleAttributeSet. It look like the line height for the cards is smaller then it should be. So I was thinking that the Swing may have some bad support for unicode characters so I added an 'M' character between the cards with the same style as the cards. After that everything seemed to work fine.
This is an example how I print out the cards (cards are printed in a loop):
StyledDocument doc = jTextPane.getStyledDocument();
doc.insertString(doc.getLength(), "Karty na stole: \n", attributeSet);
doc.insertString(doc.getLength(), "🂱🂪🂫🂠\n" , attributeSetForCards);
Cards are sometimes overflowing from their lines:
Here you can see they are correctly outputed:
Code below is the example of that bug, but the bug occure just sometimes. I noticed that Thread.sleep() has some inpact on it, because a propability of that happening increased with this command. As you can see in the code below, there is no Thread.sleep() command and the bug appears anyway.
(I changed the cards unicode for letter "M")
Here is code example :
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class StackOverflowProblem {
public static void main(String[] args) throws BadLocationException {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
int x = 0;
while (x < 100) {
x++;
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 5; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
if (i > 1) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
}
}
doc.insertString(doc.getLength(), "\n", keyWord);
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
for (int i = 0; i < 2; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class StackOverflowProblem {
public static void main(String[] args) throws BadLocationException {
JFrame frame = new JFrame();
frame.setSize(1500, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setFontFamily(attributeSet, getFontFamily());
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
String[][] obrazkyKariet = new String[4][14];
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[0][ii] = new String(Character.toChars(ii + 127137));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[1][ii] = new String(Character.toChars(ii + 127153));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[2][ii] = new String(Character.toChars(ii + 127169));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[3][ii] = new String(Character.toChars(ii + 127185));
}
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 14; j++) {
if (i == 0 || i == 3) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
} else {
StyleConstants.setForeground(attributeSet, Color.RED);
}
doc.insertString(doc.getLength(), obrazkyKariet[i][j], attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
public static String getFontFamily() {
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
for (Font font : fonts) {
if (font.canDisplay(127137)) {
System.out.println("First compatible font: " + font.getFamily());
return font.getFamily();
}
}
return "";
}
}
Upvotes: 0
Views: 306
Reputation: 324118
Swing is single threaded.
So the creation of all Swing components and updates to the components or its model must be done on the Event Dispatch Thread (EDT)
, otherwise random problems can appear.
What appears to be happening here is that the Document has not been completely updated before another insertString(…)
method is invoked and some text is not being inserted into the proper location in the Document.
Read the section from the Swing tutorial on Concurrency for more information about the EDT.
So the solution is to place your code for execution on the EDT
. The code should be something like:
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class StackOverflowProblem {
private static void createAndShowGUI() throws Exception
{
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
int x = 0;
while (x < 100) {
x++;
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 5; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
if (i > 1) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
}
}
doc.insertString(doc.getLength(), "\n", keyWord);
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
for (int i = 0; i < 2; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
createAndShowGUI();
}
catch(Exception e) { System.out.println(e); }
}
});
}
}
The invokeLater(…)
places the code on the EDT
.
Upvotes: 3