By default JLabel cuts off text on the right with 3 dots, if the text is too long to be displayed completely like this:
(Image is from a small backup application I'm working on). As you can see the last JLabel above the "Cancel"-button is cut off on the right. This behavior is clearly not desirable, as the more relevant part of the text is cut off.
I'd like the resulting label to look like in this image (right column, sry for the bad resolution):
Source
So far I've tried to change the alignment of the text within the label to JLabel.RIGHT, alter the components orientation to ComponentOrientation.RIGHT_TO_LEFT, set horizontalTextPosition, all to no avail:
import javax.swing.*;
import java.awt.*;
public class Backup
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(()->{
JLabel label = new JLabel("Some test text 1 2 3 4 5 6 7 8 9 10 abcdefghjiklmnopqrstuvwxyz", JLabel.RIGHT);
label.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
label.setHorizontalTextPosition(JLabel.RIGHT);
JFrame frame = new JFrame();
frame.add(label);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
What I've tried as well, using HTML:
<html>
<body>
<p style="width:300px;overflow-x:hidden;direction:rtl">
kladsjhglakjsjdghlekfalksjdvhlkjdsnkljhsdlkvjhasdkjhfslkdjhcksdjhvflkasjvhlkajdlkajvsdhvlkjsadhaaaaaaaaaaaaa
</p>
</body>
</html>
While this works just in the way it's supposed to in my browser, swing doesn't seem to support a sufficient set of style-properties to support this behavior.
It shouldn't be too hard to code a own implementation that fulfills the requirement of doing precisely this. Nevertheless I was wondering whether there was a "swing-way" of achieving this.
This doesn’t provide the leading ellipsis (…), but at least it is simple and clean. You can put the JLabel in a JViewport and keep it scrolled to the end at all times:
JViewport viewport = new JViewport();
viewport.setView(label);
viewport.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent event) {
int width = viewport.getWidth();
Dimension size = label.getPreferredSize();
viewport.setViewPosition(new Point(size.width - width, 0));
}
});
Using the excellent answer from trashgod (https://stackoverflow.com/a/3597688/567496), here is a simple implementation of a BasicLabelUI that creates a left-side ellipsis.
It does use Apache's StringUtils.reverse(text), but only for convenience. It could be replaced with calls to StringBuilder(text).reverse().toString().
static class LeftEllipsisUI extends BasicLabelUI {
#Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR) {
return StringUtils.reverse(super.layoutCL(label, fontMetrics, StringUtils.reverse(text), icon, viewR, iconR, textR));
}
}
You can add this and see if it is what you need:
label.setHorizontalAlignment(SwingConstants.LEFT);
By the documentation:
Sets the alignment of the label's contents along the X axis.
Related
I am trying to align the position of text within a JTextArea and a JButton, but with everything I tried, either nothing happens, or the alignment is still slightly off.
Here is what is looks like:
(You can see with the highlighted option that the JButton (center) is slightly lower than the two JTextAreas on either side.)
Here is some code:
categoryFile[i][j] = tempButton;
categoryFile[i][j].setBackground(Color.white);
categoryFile[i][j].setForeground(Color.black);
categoryFile[i][j].setOpaque(true);
categoryFile[i][j].setFocusable(false);
categoryFile[i][j].setBorderPainted(false);;
categoryFile[i][j].setVerticalAlignment(SwingConstants.TOP);
categoryFile[i][j].setPreferredSize(new Dimension(500,10));
categoryFile[i][j].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
openPDFWithOptions(filePath,fileName);
}
});
JPanel listRow = new JPanel();
listRow.setBackground(Color.white);
listRow.setLayout(new BorderLayout());
listRow.setPreferredSize(new Dimension(800, 40));
JTextArea category = new JTextArea(fileElements[0]);
category.setEditable(false);
JTextArea parent = new JTextArea(fileElements[1]);
parent.setEditable(false);
listRow.add(parent,BorderLayout.WEST);
listRow.add(categoryFile[i][j],BorderLayout.CENTER);
listRow.add(category,BorderLayout.EAST);
categoryLists[i].add(listRow,c);
Right now I am using categoryFile[i][j].setVerticalAlignment(SwingConstants.TOP) to change the position of the JButton, which ALMOST works. I've also tried changing the vertical alignment of the JTextAreas, but nothing changed.
How can I align the text within these components?
Quickest way to fix this would probably be to just add some padding on the 1st and third columns to set all the text to the same height. See Jpanel Padding
I'm trying to write a code that does the following:
If I click on the String C(JLabel) whose starting position is (100,100), the String moves WITHIN the boundaries of JFrame. The code itself wasn't hard to implement but I'm having issues with setting the (x,y) for JLabel so that any Part of the String "C" doesn't get cut off.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class adfadf extends JFrame{
JLabel text = new JLabel("C");
Container container = getContentPane();
public adfadf(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
container.setLayout(null);
MyMouseListener mml = new MyMouseListener();
text.addMouseListener(mml);
text.setLocation(100,100);
text.setSize(30,30);
add(text);
setSize(400,400);
setVisible(true);
}
public static void main(String[] args) {
new adfadf();
}
}
class MyMouseListener extends MouseAdapter{
#Override
public void mouseClicked(MouseEvent e){
JLabel text = (JLabel)e.getSource();
int x = (int)(Math.random()*(400-30));
int y = (int)(Math.random()*(400-30));
text.setLocation(x,y);
}
}
How should I change
int x = (int)(Math.random()*(400-30));
int y = (int)(Math.random()*(400-30));
in order to achieve what I want?
First, understanding that a JFrame is much more complex then it seems
To start with, a JFrame has a JRootPane, that contains the contentPane and JMenuBar and glassPane
This is further complicated by the fact the window's decorations are actually painted WITHIN the visible bounds of the frame, meaning that the visible area available to your content is actually smaller than the frame's size.
You can have a look at How can I set in the midst?, Graphics rendering in title bar and How to get the EXACT middle of a screen, even when re-sized for more details and examples of this.
But how does this help you? Well, now you know that you have a space of less than 400x400 to display your label in, but how much?
The simple solution is to stop using "magic" numbers, and take a look at something which is been used by the frame, the contentPane. The contentPane is managed by the the JFrame (via the JRootPane) so that it sits within the frame decorations, so you could do something more like...
JLabel text = (JLabel)e.getSource();
int width = getContentPane().getSize().width;
int height = getContentPane().getSize().height;
int x = (int)(Math.random()*(width-30));
int y = (int)(Math.random()*(height-30));
text.setLocation(x,y);
The reason for looking at the contentPane in this instance is simply because, that's the container that the label is actually added to.
This is one of the reasons why we suggest you don't use "magic" numbers, but look at the actual known values at the time you need them.
The default JLabel draws its text at the middle of its bounds. For example, if height of the label is 20, font height is 14, the Y coordinate would be (20 - 14)/2 = 3. Like this:
What should I do if want to align the text to the TOP of the JLabel bounds? Like this:
UPD:
public class LabelTest extends JFrame {
public LabelTest() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(500, 500);
setLocationRelativeTo(null);
JPanel contentPanel = new JPanel();
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.X_AXIS));
contentPanel.add(Box.createHorizontalStrut(10));
final JLabel label1 = new JLabel("JLabel");
label1.setVerticalAlignment(SwingConstants.TOP); // by the answer of Kevin Workman, doesn't help
label1.setBorder(BorderFactory.createLineBorder(Color.RED));
label1.setFont(new Font("Arial", Font.PLAIN, 14));
contentPanel.add(label1);
setContentPane(contentPanel);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LabelTest();
}
});
}
}
You should be packing the frame. If you so this, there should be no unused space in the label. If you want empty space, use an empty border
label.setBorder(new EmptyBorder(0, 0, 5, 0));
top, left, bottom, right
Also, don't set sizes, Use Layout Mangers and let them do the sizing for you. Setting sizes will give you. Setting sizes will give you a rigid look that may look and perform differently on different platforms. Layout Managers will allow your GUI to be more fluid and adaptable to different environments.
See Laying out Components Within a Container for more information on working with layouts
Also see Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
As always, the API is your best friend: http://docs.oracle.com/javase/7/docs/api/javax/swing/JLabel.html#setVerticalAlignment(int)
Edit- Based on your updated SSCCE, the problem is that your BoxLayout is shrinking the JLabel as small as it will go, so the vertical text position doesn't really matter. Try using a BorderLayout to check that.
The problem is that the insets of the JLabel are adding a small space to the top and bottom of the JLabel, so your text looks centered even though it's at the top. Here's a fix for the insets problem: How to change gap in swing label
Alright so I've got this JFrame with a screen on it. I've set the size to 800 by 800. However the window is created smaller than that. It's not a problem with the taskbar because it's not fullsize.
package sharph;
import java.awt.Dimension;
import javax.swing.JFrame;
public class Main extends JFrame {
public static String Title = "Game 1";
public static Dimension screenSize = new Dimension(800,800);
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle(Title);
frame.setSize(screenSize);
frame.setLocationRelativeTo(null);
frame.setResizable(true);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
Screen screen = new Screen();
screen.setSize(screenSize);
frame.add(screen);
frame.setVisible(true);
}
}
In the screen class the paint method draws a box around where the border should be:
//Draw border
g.setColor(Color.RED);
g.drawRect(1, 1, 799, 799);
When I run it, the window is smaller than the box and the bottom and right sides are cut off.
Note the second picture I manually re-sized to show the border difference.
I realize that I have drawn the box 1 pixel smaller on each side, but the difference is much more than 2 pixels.
This happens because the content needs to squeezed into the size of the frame minus its borders.
Checkout this question and this question for a more detailed explanation
The layout manager is also overriding the size property you set on the Screen component. In either case, you should be overriding the getPreferredSize method of the Screen class
Also, you shouldn't be relying on magic numbers or assumptions about the actual size of the component, but should, instead, be using getWidth and getHeight instead (I know, it's just for demonstration purposes)
Instead of "screen.setSize(screenSize);" type "screen.setPreferredSize(screenSize);" and then after you type "frame.setVisible(true);" type "frame.pack()". You can also remove "frame.setSize(screenSize);" if you want to.
JEditorPane seems to have a very interesting feature: it seems to track its parent width, and determines the preferred height accordingly, also if the parent IS NOT a JViewport.
By track i mean that the preferred width of the component is set to the one of its parent (maybe apart from some insets).
The ScrollableTracksViewportWidth is false.
This is the very simple code that demonstrates this fact (just copy and fix imports):
When the JFrame is resized, the preferred width of the JEditorPane (in my environement) is always frame.width-14 (of course 14 may be graphical-system specific).
q1) Tracking the parent (non viewport) width is good. Can I rely on it? As far as i know this is an undocumented feature.More! Just replace new JEditorPane() with new JTextPane(), a richer subclass of JEditorPane, and the feature disappear.
q2) It seems to me that this "tracking" happens through the "setting" of the JEditorPane size. This means that FIRST the size (width) must be set, then the preferred size height will be ok. Is it right?
q3) Why JTextPane has not this feature?
public class SSCE01 extends JFrame {
public static void main(String[] a) {
new SSCE01().setVisible(true);
}
public SSCE01() {
final JEditorPane ep = new JEditorPane();
add(ep);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
Dimension ps = getSize();
System.out.println("Frame size : " + ps.width + " x " + ps.height);
ps = ep.getPreferredSize();
System.out.println("JEditorPane preferredSize: " + ps.width + " x " + ps.height);
}
});
pack();
}
}
q4) More clear question. As hypothesized in q2, setting the size allows the tracking. But just for JEditorPane, NOT for JTextPane. How can I accomplish this for the JTextPane too?
this works:
public SSCE02() {
JEditorPane ep = new JEditorPane();
ep.setText("this is a very very long text. veeeeery long, so long that it will never fit into one 100 pixels width row");
ep.setSize(new Dimension(100,Integer.MAX_VALUE));
add(ep);
pack();
}
this doesn't. It has been used a JTextPane in place of JEditorPane:
public SSCE02() {
JEditorPane ep = new JTextPane();
ep.setText("this is a very very long text. veeeeery long, so long that it will never fit into one 100 pixels width row");
ep.setSize(new Dimension(100,Integer.MAX_VALUE));
add(ep);
pack();
}
UPDATE 1
Summary: the "track Size property" is observed in JEditorPane but nothing similar exists in JTextPane.
A little but significative step further:
Loading an HTML document into JEditorPane lets the feature disappear from JEditorPane too.
At this point, the feature seems implemented by the Document implementation and not by the JEditorPane (or JTextPane) itself! In the case of JEditorPane, the Document is javax.swing.text.PlainDocument. When you do:
URL url = HTMLInComponents01.class.getResource("sample.html");
jEditorPane1.setPage(url);
System.out.println(jEditorPane1.getDocument().getClass().getName());
you'll get:
javax.swing.text.html.HTMLDocument
I also notice that the good javax.swing.text.PlainDocument that gives us this great service of "calculating the height" of a component when the width is given through setSize" isn't assignable to a JTextPane, that expects a StyledDocument instance!
Now I'll verify which other text components are capable of using PlainDocument.
I recommend you add the component to the JFrame's content pane instead of using the add() method. Also set a layout on the content pane and it will all resize automatically.
JFrame f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new JTextPane());
Regards