Prevent JLabel with html from breaking line - java

Wondering if anyone knows how to disable line break when using html in JLabel.
Here is the code:
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.GridLayout;
public class Main
{
private static final int[] TEXT =
{ 0x05D0, 0x05B2, 0x05DC,
0x05B5, 0x05D9, 0x05DB,
0x05B6, 0x05DD
};
public static void main(String[] args)
{
String text = "";
for(int cp : TEXT)
text += Character.toString(cp);
String html = "<html>" + text + "</html>";
JLabel label = new JLabel(html);
JLabel msg = new JLabel("The text should at least go out to here.");
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2,1));
panel.add(label);
panel.add(msg);
JOptionPane.showMessageDialog(null, panel);
}
}
The bidi text breaks into two lines. I am trying to write html to a JTree node so it will support multiple font families. I can't get it to work with a JLabel. I'm thinking I might need to paint it in a cell renderer. I was hoping to get the html to work. It would make things a lot easier.
Any suggestions?
=== Edit ===
When my display setting in Windows is at 125% it breaks the line; however, when I change my display setting in Windows to 100% it does not break the line. Running 1920 x 1080 display. Anyone have any ideas? Or, is anyone able to repeat the breaking of the line?
=== Edit ===
Interestingly when I pass -Dsun.java2d.uiScale= with 1.0 or 2.0 it works. When I use 3.0, 4.0, 1.25 or 1.5 or 0.8 it does not work.

Well, I submitted a bug report. I managed to find a workaround that seems to work for all Windows Display Scale's but it is quite a hacky workaround. But it will be sufficient until the bug is fixed.
Here it is:
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.GridLayout;
import java.awt.font.FontRenderContext;
public class Main
{
private static final String TEXT =
"\u05D0\u05B2\u05DC\u05B5\u05D9\u05DB\u05B6\u05DD";
public static void main(String[] args)
{
JLabel label = new JLabel();
Graphics2D g2 = (Graphics2D)new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).getGraphics();
FontRenderContext frc = g2.getFontRenderContext();
String[] split = TEXT.split("");
double width = 0;
for(String chr : split)
width += label.getFont().getStringBounds(chr, frc).getWidth();
double scale = Toolkit.getDefaultToolkit().getScreenResolution()/96.0;
String html = "<html><p style=\"width:"+Math.ceil(width/scale)+"px;\">" + TEXT + "</p></html>";
label.setText(html);
JLabel msg = new JLabel("The text should at least go out to here.");
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2,1));
panel.add(label);
panel.add(msg);
JOptionPane.showMessageDialog(null, panel);
}
}
Note: If I don't get the individual character lengths then the width doesn't come out right in a JTree node. Also, for some reason when I use bidi text the resultant width of the nodes JLabel is about twice the width of the text. Any shorter and it does not display correctly. However, the width is correct with Non-Bidirectional text. Also, width: does not work with span tag, it only works with p tag

Related

How to get the height when knowing the width of the label in java swing?

Hi everyone i am using java swing now.
I have a problem like this:
I have a pretty long piece of text and put it in a label with the <html> tag.
<html> My text </html>
If the text is too long, it will break the line according to the width of the label.
How to calculate the number of line breaks? or height needed to display text after line breaks
This is my code
public Test() {
initComponents();
setPreferredSize(new Dimension(200, 200));
add(new JLabel("<html> "
+ "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)."
+ "</html>"));
}
How to know the exact height needed to display the text? 😢😢😢
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
Here's the GUI I created. Your code wasn't runnable. When I made it runnable, your HTML didn't specify any line breaks, so there weren't any line breaks.
Generally, you use a JTextArea for really long text. You specify the number of rows and columns that you want. By adjusting the rows and columns, you indirectly adjust the size of the component.
I put the JTextArea in a JScrollPane so my sizing of the component is independent of the length of the text. I sometimes use a JTextArea in a JScrollPane for my instructions dialogs.
Here's the complete runnable code.
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class LongJLabel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LongJLabel());
}
#Override
public void run() {
JFrame frame = new JFrame("Long JLabel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
JTextArea textArea = new JTextArea(10, 40);
textArea.setEditable(false);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setText("It is a long established fact that a reader will be "
+ "distracted by the readable content of a page when "
+ "looking at its layout. The point of using Lorem "
+ "Ipsum is that it has a more-or-less normal "
+ "distribution of letters, as opposed to using "
+ "'Content here, content here', making it look like "
+ "readable English. Many desktop publishing packages "
+ "and web page editors now use Lorem Ipsum as their "
+ "default model text, and a search for 'lorem ipsum' "
+ "will uncover many web sites still in their infancy. "
+ "Various versions have evolved over the years, "
+ "sometimes by accident, sometimes on purpose "
+ "(injected humour and the like).");
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setCaretPosition(0);
panel.add(scrollPane);
return panel;
}
}
Getting the exact required height seems to be non-trivial. Though you could use an "educated guessing" method to calculate the required height.
String text = "Your text here";
JLabel label = new JLabel("<html> "
+ text
+ "</html>");
//You can get very useful Information via the Font Metrics class
FontMetrics fm = label.getFontMetrics(label.getFont());
//Here you need a reference to your window so you can get the width of it.
//Via the stringWidth function you get the width of the text when written as one line
//Subtracting 40 is to counter the effect, that the text is broken after each line
//The (int) (...) + 1 is just to round up
int lines = (int) (fm.stringWidth(text) / (window.getWidth() - 40f)) + 1;
//last you can set the window height however you like but watch out
//since the window top margin has to be minded
window.setSize(200, lines * fm.getHeight() + 30);
I looked at the code of class javax.swing.JLabel and also performed some experiments and discovered that when the text of the JLabel starts with <html>, a client property – named html – is set and the property value has type javax.swing.text.View.
Note that if the JLabel text does not start with <html> then there is no html [client] property.
View has method getPreferredSpan which returns the width of the entire text (of the JLabel) and the height of a single line (depending on the parameter used when calling the method – see below code).
Assuming the total width you wish to assign is arbitrary (in the code in your question I believe that the width is 200), you can calculate how many lines will be required. From there you can calculate the required height.
I still haven't discovered why the calculated number of lines is 2 smaller than the required number so I added 2 to the calculated value.
Also note that I set the preferred size of the JLabel, rather than the window (i.e. JFrame) and also call method pack so that the window size will suit the JLabel preferred size.
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.text.View;
public class Test {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
String text = "<html>It is a long established fact that a reader will be distracted " +
"by the readable content of a page when looking at its layout. The " +
"point of using Lorem Ipsum is that it has a more-or-less normal " +
"distribution of letters, as opposed to using 'Content here, content " +
"here', making it look like readable English. Many desktop publishing " +
"packages and web page editors now use Lorem Ipsum as their default " +
"model text, and a search for 'lorem ipsum' will uncover many web " +
"sites still in their infancy. Various versions have evolved over the " +
"years, sometimes by accident, sometimes on purpose (injected humour " +
"and the like).";
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel(text);
View view = (View) label.getClientProperty("html");
float textWidth = view.getPreferredSpan(View.X_AXIS);
float charHeight = view.getPreferredSpan(View.Y_AXIS);
double lines = Math.ceil(textWidth / 200) + 2;
double height = lines * charHeight;
label.setPreferredSize(new Dimension(200, (int) Math.ceil(height)));
frame.add(label);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
}

Java - TitledBorder takes up too much vertical space (on Windows only)

I want to use a TitledBorder around a JTextField without it taking up too much vertical space.
In the top it applies way more spacing for title font than is needed. Also in the bottom there's a whopping 4 pixels I can't use.
This occurs only on Windows; on Mac OSX the example below looks fine while on W10 the JTextField content is horribly cropped.
Can I reduce this in any way?
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
public class MoreSpace {
static public void main(String args[]) {
EmptyBorder eb = new EmptyBorder(0, 0, 0, 0);
TitledBorder tb = new TitledBorder(eb, "Title");
Font font = new Font("dialog", Font.BOLD, 10);
tb.setTitleFont(font);
JTextField textField = new JTextField();
textField.setPreferredSize(new Dimension(300,26));
textField.setBorder(tb);
textField.setText("I cant breathe in here");
JOptionPane.showMessageDialog(null, textField, "",JOptionPane.PLAIN_MESSAGE);
}
}
Create a custom TitledBorder class(from package javax.swing.border) and reduce the maximum EDGE_SPACING as desired.
// Space between the border and the component's edge
static protected final int EDGE_SPACING = 2;
this means 2 pixels above and below as padding by default for the TitledBorder. This should explain the 4 pixels you are seeing.
Setting EDGE_SPACING to 0 will do what you are looking for. :)

How to set JButton to change color gradually when clicked

I wanted to create JButton to change color every time it is clicked but it doesn't change after second click.
It is strange because with Random().nextInt(250) instead of i it works.
What could be the problem?
Here's the code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel jp = new JPanel();
jp.setLayout(new BorderLayout(100, 100));
JButton l = new JButton("Hello");
l.setBackground(new Color(245, 12, 53));
jp.add(l, BorderLayout.EAST);
jf.add(jp);
jf.setSize(200, 200);
jf.setLocationRelativeTo(null);
jf.setVisible(true);
l.addActionListener(new ActionListener() {
Integer i = new Integer(0);
Color c = new Color(150, 10, i);
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (i < 200) {
i += 50;
c = new Color(150, 10, i);
l.setBackground(c);
} else
i = 0;
}
});
}
}
I debugged your code and saw that the value of c change, every time I click the button. The first value is (r=150,g=10,b=50), then turns into (r=150,g=10,b=100), then (r=150,g=10,b=150) etc.
This means that the color is indeed changing. It's just that the difference is too small for you to notice.
So why does random.nextInt work?
With a random value in the blue component. The value can jump very suddenly from 0 to 200. The color difference is so large that your eyes can see it. But with a gradual change of 50 every time, you only notice it the first time.
Just test it with new Color(0, 0, i). I think that will make a bigger difference. It will go from black to blue!
Works fine for me.
Although the code should probably be something like:
if (i < 200)
i += 50;
else
i = 0;
c = new Color(150, 10, i);
l.setBackground(c);
Otherwise there will be one click that doesn't change the color.
You may want to consider using HSL Color this will allow you to change the Color in a more meaningful way by either changing the hue of the Color or shade/tone of the Color.

Java. Prevent enlarging of Button

How to prevent Button from UpSize? I use a vertical Box with 2 lines, in first line → horizontal Box with many controls, in second line → Button, but button very enlarged, how to prevent this irritating behavior?
I was tried to set maximum size of button, it is works, but How to calculate this size correctly?
Sorry, I bad speak English.
Example:
import java.awt.Button;
import java.awt.Frame;
import java.awt.Label;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JProgressBar;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
public class TEST{
public static void main(String[] args){
Frame mainWindow = new Frame("hello");
Box vertBox = new Box(BoxLayout.Y_AXIS);
mainWindow.add(vertBox);
Box firstLine = new Box(BoxLayout.X_AXIS);
vertBox.add(firstLine);
SpinnerModel sm = new SpinnerNumberModel(100, 0, 200, 1);
for(int i = 0; i < 10; i++){
firstLine.add(new JSpinner(sm));
firstLine.add(new Label("Hello"));
}
Box secondLine = new Box(BoxLayout.X_AXIS);
vertBox.add(secondLine);
secondLine.add(new JProgressBar());
secondLine.add(new Button("RUN-THIS"));
mainWindow.pack();
mainWindow.setVisible(true);
}
}
Button, but button very enlarged, how to prevent this irritating behavior?
Don't use AWT components in a Swing application!
Swing components start with "J". You should be using JFrame, JLabel, JButton. This will fix your problem. A JButton will automatically calculate the maximum size equal to the preferred size and BoxLayout will respect this.
Also, class name should only start with a single upper case character. "TEST" should be "Test".

Issues creating java GUI with while loop

I am creating a java GUI which is a fortune teller. The GUI will spit out one of twelve fortunes every time you click the "get my fortune" button, the strings will never repeat back to back, can can repeat later after other strings have gone before it. I have made already for the most part. But now I am having some trouble creating the while loops to display the strings without repeating. I have looked at my book which didn't really help. If you guys could point me in the right direction,it would be much appreciated. Thanks!
I entered all of the code so you can see the variables used. But my question starts at class RndButtonListener.
package FortuneTellerRunner;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
*
* #author a3cal_000
*/
class FortuneTellerFrame extends JFrame
{
final private JPanel mainPnl, titlePnl, displayPnl, buttonPnl, imagePnl;
final private JButton quitBtn, rndBtn;
final private JLabel titleLbl, iconLbl;
final private JTextArea displayTa;
final private JScrollPane scroller;
public String[] fortune = new String [12];
int newIndex, oldIndex;
private static final int HEIGHT = 250;
private static final int WIDTH = 450;
public FortuneTellerFrame()
{
setSize(WIDTH, HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPnl = new JPanel();
mainPnl.setLayout(new BorderLayout());
displayPnl = new JPanel();
buttonPnl = new JPanel();
titlePnl = new JPanel();
ImageIcon icon = new ImageIcon("FortuneTellerIcon.JPEG");
iconLbl = new JLabel(icon);
titleLbl = new JLabel("Fortune Teller!");
displayTa = new JTextArea();
imagePnl = new JPanel();
scroller = new JScrollPane();
// Create the layout of the title panel
titlePnl.setLayout(new GridLayout(2,1));
add(mainPnl);
// Set the label to the panel.
titlePnl.add(titleLbl);
titlePnl.add(iconLbl);
// add the panel to the main panel.
mainPnl.add(titlePnl, BorderLayout.NORTH);
mainPnl.add(scroller, BorderLayout.CENTER);
mainPnl.add(displayTa, BorderLayout.CENTER);
// Create the "Get my fortune button.
rndBtn = new JButton("Get My Fortune!");
quitBtn = new JButton("Quit");
// Add the buttons to the buttonPnl in grid layout.
buttonPnl.add(rndBtn);
buttonPnl.add(quitBtn);
// Create the grid layout for the button panel.
buttonPnl.setLayout( new GridLayout(1, 2));
// Add the button panel to the grid layout, South.
mainPnl.add(buttonPnl, BorderLayout.SOUTH);
ActionListener listener = new RndButtonListener();
rndBtn.addActionListener(listener);
quitBtn.addActionListener(listener);
}
class RndButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
fortune[0] = "He who throws dirt is losing ground.";
fortune[1] = "You will find the love of your life in food.";
fortune[2] = "Do or do not, there is no try.";
fortune[3] = "Tomorrow is a better day to try anything of importance.";
fortune[4] = "Life's not about how hard you can hit, but how hard you can get hit and keep moving forward.";
fortune[5] = "You can't be late until you show up.";
fortune[6] = "If you think things can't get worse it's probably only because you lack sufficent imagination.";
fortune[7] = "If youre at the top it means you have further to fall.";
fortune[8] = "Even in last place, youre still in the race.";
fortune[9] = "The road to riches is paved on the failures of others.";
fortune[10] = "If you feel like your going no where, get off the treadmill.";
fortune[11] = "Thinking about going to the gym is just as good as going.";
Random rnd = new Random(fortune.length);
do
{
newIndex = rnd.nextInt(fortune.length);
}
while(newIndex == oldIndex);
do
{
System.out.println(fortune[newIndex]);
displayTa.append(fortune[newIndex] + "||");
displayTa.updateUI();
mainPnl.updateUI();
oldIndex = newIndex;
}
while(newIndex != oldIndex);
class QuitButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
System.exit(0);
}
}
}
}
}
The basic problem is you are re-creating the Random with the same seed each time, which is generally creating the same random sequence over and over again.
Instead try using...
do {
newIndex = (int) Math.round(Math.random() * (fortune.length - 1));
} while (newIndex == oldIndex);
You also don't need the second loop, it's just clutter that confuses the situation.
You may also find that...
displayTa.append(fortune[newIndex] + "\n");
produces nicer output (IMHO)
You may also wish to take a look at How to use Scroll Panes
Your program run fine, but this is a problem, fortune.length is a random seed which return me only 6 and 8 when I later called Random.nextInt().
Random rnd = new Random(fortune.length);
Do it this way
Random rnd = new Random();
and also consider the formatting solution given by MadProgrammer.
Random() gives you same number pattern. Try Random(System.currentTimeMillis()). It uses current time as seed, so you can get real random numbers.
I did something similar to this just today, so let's see if I can remember... I made an ArrayList of type int of how many items I had (fortunes)
ArrayList<Integer> fortuneSeq = new ArrayList<Integer>();
Then add in some numbers starting from 0 to code for the fortunes.
for(int i = 0; i < fortune.length; i++) {
fortuneSeq.add(i);
}
Then I used the shuffle() method from the Collections class to randomize the list.
Collections.shuffle(fortuneSeq);
After that, just loop through to access the fortunes.
for(int i = 0; i < fortune.length; i++) {
System.out.println(fortune[fortuneSeq.get(i)]);
//...
}
Edit: Silly autocorrect, you don't like programmers.
Edit: Fixed some furtunes instead of fortunes and fixed println statement.

Categories

Resources