I am trying to make a custom TabbedPaneUI by extending BasicTabbedPaneUI
I am having an issue with setting the TextColor, When ever I set the TextColor it shows a border when the pane is focused and enabled.
as you can see the pane that has the title Test is enabled/selected Pane and it has a white border around it and I do not want the border. I have already overrided paintTabBorder
#Override
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
//We do nothing here to disable painting the Tab Border
}
here is how I am overriding paintText to change the Text Color
#Override
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
v.paint(g, textRect);
} else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
//Change the text to White here <- Causing Border Issues
g.setColor(Color.WHITE);
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
} else { // tab disabled
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x - 1, textRect.y + metrics.getAscent() - 1);
}
}
}
g.setColor(Color.WHITE); is causing the border issue, I have confirmed this by setting the Color to something else rather then White and the border was also that color aswell as the Text.
Just draw another empty title with the background color to effectively hide the border.
#Override
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
v.paint(g, textRect);
} else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
//Change the text to White here
g.setColor(Color.WHITE);
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
//Remove Border Issues
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
"", mnemIndex, //<-- empty title here
textRect.x, textRect.y + metrics.getAscent());
} else { // tab disabled
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x - 1, textRect.y + metrics.getAscent() - 1);
}
}
}
After a bit of research, I have figured out how to fix the border issue I had to set the graphics color after drawing the String. g.setColor(selectColor);
//Change the text to White here <- Causing Border Issues
g.setColor(Color.WHITE);
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
//reset the color to selectedColor
g.setColor(selectColor);
Related
I was wondering if I can make a partial bold text for example:
Please make your assignment before 21 of july
I have not tried anything yet, because i just have no clue how to do this.
Here is my code currently for rendering text:
public void renderText(int page, String text, String font, int fontSize, Color color, int width, int height) {
final Graphics2D g;
if (page == 1) {
g = (Graphics2D) outsideFlyer.getGraphics();
g.drawImage(outsideFlyer, 0, 0, null);
} else {
g = (Graphics2D) insideFlyer.getGraphics();
g.drawImage(insideFlyer, 0, 0, null);
}
if (font == null) {
g.setFont(new Font(font, Font.PLAIN, fontSize));
} else if ("bold".equals(font)) {
g.setFont(new Font(null, Font.BOLD, fontSize));
} else {
g.setFont(new Font(font, Font.PLAIN, fontSize));
}
g.setColor(color);
for (String txt : text.split("\n")) {
g.drawString(txt, width, height += g.getFontMetrics().getHeight());
}
g.dispose();
}
Is it possible to split the text in some way?
I would love to have something like what happens on a lot of websites like everything inbetween * gets bold or in some way.
I have JLabels in a constrained space (JTable) and when the text inside the label is too long, it's truncated. Is there a way to make the text fit in the allotted space by only horizontal squishing?
See the upper Jlabel in these examples:
The text is HTML formatted so I can't just drawstring on a custom JPanel component myself. There's no icon.
Since I've solved this question while typing it, in accordance with meta I'll share the answer.
I set this as the UI for the JLabel:
It renders the text to an off-screen image, then resizes that image to the JLabel's proportions.
[Edit] This doesn't work correctly with transparent labels or labels with empty HTML text.
// Copied and modified from BasicLabelUI
private static class SquishLabelUI extends BasicLabelUI {
private final Rectangle paintIconR = new Rectangle();
private final Rectangle paintTextR = new Rectangle();
private String layout(JLabel label, FontMetrics fm, int width, int height) {
Insets insets = label.getInsets(null);
String text = label.getText();
Rectangle paintViewR = new Rectangle(insets.left,
insets.top,
width - (insets.left + insets.right),
height - (insets.top + insets.bottom));
paintIconR.setBounds(0, 0, 0, 0);
paintTextR.setBounds(0, 0, 0, 0);
return layoutCL(label, fm, text, null, paintViewR, paintIconR, paintTextR);
}
#Override
public void paint(Graphics g, JComponent c) {
JLabel label = (JLabel)c;
layout(label, SwingUtilities2.getFontMetrics(label, g), c.getWidth(), c.getHeight());
View v = (View)c.getClientProperty(BasicHTML.propertyKey);
Dimension size = getPreferredSize(label);
BufferedImage img = label.getGraphicsConfiguration()
.createCompatibleImage(size.width, size.height, TRANSLUCENT);
Graphics2D g2 = img.createGraphics();
try {
g2.setColor(label.getBackground());
g2.setClip(0, 0, size.width, size.height);
g2.fillRect(0, 0, size.width, size.height);
v.paint(g2, new Rectangle(0, 0, size.width, size.height));
int renderWidth = Math.min(size.width, paintTextR.width);
Image img2 = img.getScaledInstance(renderWidth, paintTextR.height, Image.SCALE_SMOOTH);
g.drawImage(img2, paintTextR.x, paintTextR.y, null);
} finally {
g2.dispose();
}
}
}
I've created this custom JToolTip for my application. When the tooltip is entirely diplayed inside a JFrame, no background is visible (expected), but when the tooltip is displayed outside the JFrame, the background will be visible. How can I have it removed either way?
I've tried setBackground(new Color(255, 255, 255, 0)); with the '0' alpha value to make sure the background is transparent, but that didn't do the trick.
The tooltip inside the frame, as expected:
The tooltip exceeding the JFrame, with the unwanted background:
The custom JTooltip:
public class DefaultToolTip extends JToolTip {
public DefaultToolTip() {
setOpaque(false);
setPreferredSize(new Dimension(275, 30));
setBackground(new Color(255, 255, 255, 0));
}
#Override
public void addNotify() {
super.addNotify();
setOpaque(false);
Component parent = this.getParent();
if (parent != null) {
if (parent instanceof JComponent) {
JComponent jparent = (JComponent) parent;
jparent.setOpaque(false);
}
}
}
#Override
public void paint(Graphics g) {
String text = getComponent().getToolTipText();
addNotify();
Graphics2D g2d = drawComponent(g);
drawText(text, g2d);
g2d.dispose();
}
private void drawText(String text, Graphics2D g2d) {
//Draw the text
int cHeight = getComponent().getHeight();
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.WHITE);
if (cHeight > getHeight())
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (getHeight() + fm.getAscent()) / 2 + 2);
else
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (cHeight + fm.getAscent()) / 2 + 2);
}
private Graphics2D drawComponent(Graphics g) {
//Create a round rectangle
Shape round = new RoundRectangle2D.Float(0, 8, getWidth(), getHeight(), 8, 8);
//Draw the background
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fill(round);
//Draw the left triangle
Point p1 = new Point(getWidth() / 2, getHeight() - 30);
Point p2 = new Point(getWidth() / 2 + 8, getHeight() - 20);
Point p3 = new Point(getWidth() / 2 - 8, getHeight() - 20);
int[] xs = {p1.x, p2.x, p3.x};
int[] ys = {p1.y, p2.y, p3.y};
Polygon triangle = new Polygon(xs, ys, xs.length);
g2d.fillPolygon(triangle);
return g2d;
}
}
Solution
A few things have changed to have the tooltip behave as expected. the paint method has been replaced by the paintComponent method, the addNotify call was removed, the method updated to fetch the window of the component and to give it a transparent background. setBorder(BorderFactory.createEmptyBorder()); was also needed to remove the components default border.
public class DefaultToolTip extends JToolTip {
public DefaultToolTip() {
setOpaque(false);
setPreferredSize(new Dimension(275, 30));
setBackground(new Color(255, 255, 255, 0));
setBorder(BorderFactory.createEmptyBorder());
}
#Override
public void addNotify() {
super.addNotify();
setOpaque(false);
Component parent = this.getParent();
if (parent != null) {
if (parent instanceof JComponent) {
JComponent jparent = (JComponent) parent;
jparent.setOpaque(false);
}
}
Window window = SwingUtilities.windowForComponent(this);
try {
window.setBackground(new Color(255, 255, 255, 0));
} catch (IllegalComponentStateException e) {
//Do nothing
}
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
String text = getComponent().getToolTipText();
Graphics2D g2d = drawComponent(g);
drawText(text, g2d);
g2d.dispose();
}
private void drawText(String text, Graphics2D g2d) {
//Draw the text
int cHeight = getComponent().getHeight();
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.WHITE);
if (cHeight > getHeight())
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (getHeight() + fm.getAscent()) / 2 + 2);
else
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (cHeight + fm.getAscent()) / 2 + 2);
}
private Graphics2D drawComponent(Graphics g) {
//Create a round rectangle
Shape round = new RoundRectangle2D.Float(0, 8, getWidth(), getHeight(), 8, 8);
//Draw the background
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fill(round);
//Draw the left triangle
Point p1 = new Point(getWidth() / 2, getHeight() - 30);
Point p2 = new Point(getWidth() / 2 + 8, getHeight() - 20);
Point p3 = new Point(getWidth() / 2 - 8, getHeight() - 20);
int[] xs = {p1.x, p2.x, p3.x};
int[] ys = {p1.y, p2.y, p3.y};
Polygon triangle = new Polygon(xs, ys, xs.length);
g2d.fillPolygon(triangle);
return g2d;
}
}
A note however, super.paintComponent(g) was commented out, since it would draw text another time.
Don't know if any of these will help but:
Don't override paint(...). Custom painting is done by overriding paintComponent(...).
Invoke super.paintComponent(...) as the first statement
Don't invoke addNotify() in a painting method. A painting method is for painting only.
with the '0' alpha value to make sure the background is transparent,
Swing components don't know how to handle transparent backgrounds. Just make the component non-opaque.
When the tooltip overlaps the component. The tooltip is actually added to a JWindow before it is displayed. So in your addNotify() logic, you can search for the window and make it transparent.
Check out:
Window window = SwingUtilities.windowForComponent(...);
I am implementing my own Look&Feel for a JTabbedPane using BasicTabbedPaneUI.
Following this tutorial, I want to add close buttons at the end of my tabs.
So far, I have managed to paint my closing icons on the right of the tab but it overlays my tab title. Therefore, I would like to reduce the width of the Rectangle used for the textRect parameter in the overridden method paintTab().
I have tried this but it has no effect:
#Override
protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex,
Rectangle iconRect, Rectangle textRect) {
//reduce textrect width to leave space for close icon
textRect.setSize(textRect.width - (2 * WIDTHDELTA + icon.getIconWidth()), textRect.height);
super.paintTab(g, tabPlacement, rects, tabIndex, iconRect, textRect);
Rectangle tabRect = rects[tabIndex];
// Calculate the coordinates where the button should be.
int dx = tabRect.x + tabRect.width - icon.getIconWidth() - WIDTHDELTA;
int dy = tabRect.y + (tabRect.height - icon.getIconHeight()) / 2;
//Paint the Close button
icon.paintIcon(tabPane, g, dx, dy);
}
How and where can I shrink the rectangle used to paint the tab's text?
Try to play with the fields of BasicTabbedPaneUI
protected Insets tabInsets;
protected Insets selectedTabPadInsets;
protected Insets tabAreaInsets;
protected Insets contentBorderInsets;
The tabInsets are used in the method you can try to override
protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics)
Here is the solution I came up with:
private String clipText(FontMetrics metrics, String text) {
String clipped = "...";
if (metrics.stringWidth(clipped) < MAX_TEXT_WIDTH) {
StringBuilder sb = new StringBuilder(clipped);
int index = 0;
for (char c : text.toCharArray()) {
sb.insert(index, c);
if (metrics.stringWidth(sb.toString()) > MAX_TEXT_WIDTH) {
clipped = sb.deleteCharAt(index).toString();
break;
}
index++;
}
}
return clipped;
}
I use it in:
#Override
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics,
int tabIndex, String title, Rectangle textRect, boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
v.paint(g, textRect);
} else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
g.setColor(isSelected ? ANZ_BLUE : Color.WHITE);
String text;
int rectX;
if (metrics.stringWidth(title) <= MAX_TEXT_WIDTH) {
text = title;
rectX = textRect.x - (icon.getIconWidth() / 2); //center text
} else {
text = clipText(metrics, title); //clip text
rectX = textRect.x;
}
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemIndex, rectX,
textRect.y + metrics.getAscent());
} else { // tab disabled
Color bg = tabPane.getBackgroundAt(tabIndex);
g.setColor(bg.brighter());
BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemIndex, rectX,
textRect.y + metrics.getAscent());
g.setColor(bg.darker());
BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemIndex, rectX - 1,
textRect.y + metrics.getAscent() - 1);
}
}
}
It works fine for me.
I'm trying to paint a rectangle on my application in a red shade but I need to make it sort of transparent so that the component under it will still show. However I still want that some colour will still show. The method where I'm drawing is the following:
protected void paintComponent(Graphics g) {
if (point != null) {
int value = this.chooseColour(); // used to return how bright the red is needed
if(value !=0){
Color myColour = new Color(255, value,value );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
else{
Color myColour = new Color(value, 0,0 );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
}
}
Does anyone know how I can make the red shade a bit transparent? I don't need it completely transparent though.
int alpha = 127; // 50% transparent
Color myColour = new Color(255, value, value, alpha);
See the Color constructors that take 4 arguments (of either int or float) for further details.
Try this: (but it will works for Graphics2D objeccts not for Graphics)
protected void paintComponent(Graphics2D g) {
if (point != null) {
int value = this.chooseColour(); // used to return how bright the red is needed
g.setComposite(AlphaComposite.SrcOver.derive(0.8f));
if(value !=0){
Color myColour = new Color(255, value,value );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
else{
Color myColour = new Color(value, 0,0 );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
g.setComposite(AlphaComposite.SrcOver);
}
}