Undecorated JFrame having problems with resizing - java

I'm making a desktop app (a Markdown editor) in Java, and I'm having problems on frame resizing. The frame I'm working on is undecorated. On the left side of the frame is a title bar. The content pane uses a FlowLayout(FlowLayout.LEFT, 0, 0) as the
default layout. My problem is when I use the following code to achive resizing function, I can only resize my frame on X and Y axis but not both sides at the same time (when you put your cursor on a vertex of the frame), and the layout isn't working. My goal is to complete a full-functioned resizing system which you can drag and drop the edges of the frame to adjust the size of it.
My Code:
package xmark.ui;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MyFrame extends JFrame {
private final TitleBar titleBar = new TitleBar(); // public class TitleBar extends JPanel
/**
* The original content pane on the frame. Is a holder of the title bar.
*/
public JPanel contentPane = (JPanel) getContentPane();
/**
* The default constructor. No needs to manually pass in any arguments
* because it automatically decorates the frame and sets up the title
* bar. There are two private methods used in the constructor: {#code setUI()}
* and {#code titleBar()}.
*/
public MyFrame() {
setUI();
titleBar();
}
private void setUI() {
// Basic method settings
setSize(new Dimension(1200, 750));
setMinimumSize(new Dimension(120, 75));
setLocationRelativeTo(null);
setUndecorated(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
contentPane.setBackground(UIUtilities.BASE_COLOR); // UIUtilities is a ui utility class for this app
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
// Sizing function
contentPane.addMouseMotionListener(new MouseAdapter() {
boolean top = false;
boolean down = false;
boolean left = false;
boolean right = false;
Point draggingAnchor = null;
#Override
public void mouseMoved(MouseEvent e) {
double mouseY = e.getPoint().getY();
double mouseX = e.getPoint().getX();
if (mouseY > 1 && mouseY < 6) {
setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
top = true;
} else if (Math.abs(mouseY - getSize().getHeight()) <= 5) {
setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
down = true;
} else if (mouseX > 1 && mouseX < 6) {
setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
left = true;
} else if (Math.abs(mouseX - getSize().getWidth()) <= 5) {
setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
right = true;
} else {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
draggingAnchor = new Point(e.getX() + contentPane.getX(), e.getY() + contentPane.getY());
top = false;
down = false;
left = false;
right = false;
}
}
#Override
public void mouseDragged(MouseEvent e) {
Dimension dimension = getSize();
if (top) {
dimension.setSize(dimension.getWidth(), dimension.getHeight() - e.getY());
setSize(dimension);
setLocation(getLocationOnScreen().x, getLocationOnScreen().y + e.getY());
} else if (down) {
dimension.setSize(dimension.getWidth(), e.getY());
setSize(dimension);
} else if (left) {
dimension.setSize(dimension.getWidth() - e.getX(), dimension.getHeight());
setSize(dimension);
setLocation(getLocationOnScreen().x + e.getX(), getLocationOnScreen().y);
} else if (right) {
dimension.setSize(e.getX(), dimension.getHeight());
setSize(dimension);
} else {
setLocation(e.getLocationOnScreen().x - draggingAnchor.x, e.getLocationOnScreen().y - draggingAnchor.y);
}
}
});
}
private void titleBar() {
contentPane.add(titleBar);
}
public static void main(String[] args) {
new MyFrame().setVisible(true);
}
}

Related

JScrollPane minimum width inside JSplitPane

I'm trying out the JSplitPane with a couple of scrollable side-by-side JTables.
However I'm experiment a behavior where the JScrollPane gets shrinked too much, as per gif.
Notice the left component, that besides having a minimum width of 250px, continues shrinking.
The relevant code is
final var objetsTable = new JTable();
final var objectsScrollPane = new JScrollPane(objetsTable);
objectsScrollPane.setMinimumSize(new Dimension(250, 0));
objectsScrollPane.setPreferredSize(new Dimension(400, 300));
final var stepsTable = new JTable();
final var stepsScrollPane = new JScrollPane(stepsTable);
stepsScrollPane.setMinimumSize(new Dimension(150, 0));
stepsScrollPane.setPreferredSize(new Dimension(200, 300));
final var splitPane = new JSplitPane();
splitPane.setLeftComponent(objectsScrollPane);
splitPane.setRightComponent(stepsScrollPane);
splitPane.setResizeWeight(1.0);
How can I avoid the JScrollPanes getting shrinked too much in this case?
The getMinimumSize called on a JSplitPane returns a size which is actually taking into account the minimum size of its left and right Components, plus the divider size. So one way to maybe solve your problem would be to make your JSplitPane implement Scrollable (in order to make it respect the minimum size of itself) and add it to a JScrollPane. This way you can ensure that the minimum size is respected and when the user continues shrinking the Scrollable JSplitPane past its minimum size, then the scroll bars will show up.
Here is some working code:
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class Main {
private static class MyScrollableSplitPane extends JSplitPane implements Scrollable {
private int maxUnitIncrement = 10;
public void setMaxUnitIncrement(final int pixels) {
maxUnitIncrement = pixels;
}
public int getMaxUnitIncrement() {
return maxUnitIncrement;
}
/**
* This is being constantly checked by the scroll pane instead of the
* getPreferredScrollableViewportSize...
*/
#Override
public Dimension getPreferredSize() {
final Dimension minSz = getMinimumSize(),
curSz = getSize();
curSz.width = Math.max(curSz.width, minSz.width);
curSz.height = Math.max(curSz.height, minSz.height);
return curSz;
}
/**
* This is only checked once (at the beginning).
*/
#Override
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxUnitIncrement;
} else {
return visibleRect.height - maxUnitIncrement;
}
}
#Override
public boolean getScrollableTracksViewportWidth() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().width < ((JViewport) parent).getWidth());
}
#Override
public boolean getScrollableTracksViewportHeight() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().height < ((JViewport) parent).getHeight());
}
}
private static void createAndShowGUI() {
/*Since I don't add any Components to the 'left' and 'right' panels, I am going to set the
preferred size of them. This is only for demonstrating the concept. Setting the minimum size
though is somewhat required by the JSplitPane itself.*/
final JPanel left = new JPanel();
left.setMinimumSize(new Dimension(150, 100));
left.setPreferredSize(new Dimension(200, 200));
final JPanel right = new JPanel();
right.setMinimumSize(new Dimension(300, 100));
right.setPreferredSize(new Dimension(400, 200));
final JSplitPane split = new MyScrollableSplitPane();
split.setBorder(BorderFactory.createLineBorder(Color.CYAN.darker(), 3));
split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
split.setLeftComponent(left);
split.setRightComponent(right);
final JFrame frame = new JFrame("MyScrollableSplitPane demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(split));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}

Not letting graphics out of JFrame boundaries

Although there are questions similar, I think mine is slightly different because of how I have my code set up. I have a JFrame within my main method. However, I only have JPanel in my constructor. I tried to make some of my variables static so that I could access them in the main method and say, for instance, if the x-coordinate of this graphic plus its width is greater than frame.getWidth().. but that won't work for some reason. I don't want to bombard anyone with code so I will just try to put the main information and if you need more, I'll update it.
package finalProj;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Ellipse2D;
public class nonaMaingamePractice extends JPanel implements ActionListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static Ellipse2D ellipse;
static Toolkit tools = Toolkit.getDefaultToolkit();
static int screenWidth = (int)(Math.round(tools.getScreenSize().getWidth()));
static int screenHeight = (int)(Math.round(tools.getScreenSize().getHeight()));
private static Rectangle paddleRect;
JLabel text = new JLabel("cool");
Timer timeMove = new Timer(1, this);
Timer timeBall = new Timer(10, new timeBall());
private static double x = screenWidth/2, y = (screenHeight*0.8), xx = 0, yy = 0, score = 0, Ox = screenWidth/2, Oy = screenHeight/2, Oyy = 0, width = 100, height = 30;
public nonaMaingamePractice(){
setLayout(new BorderLayout());
timeBall.start();
timeMove.start();
addKeyListener(this);
setFocusable(true);
JPanel panelNorth = makePanel();
panelNorth.setBackground(Color.CYAN);
add(panelNorth, BorderLayout.NORTH);
JLabel scoreLabel = new JLabel("Score: " + score);
panelNorth.add(scoreLabel);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
paddleRect = new Rectangle((int)x, (int)y, (int)width, (int)height);
ellipse = new Ellipse2D.Double(Ox, Oy+Oyy, 50, 50);
Graphics2D graphics = (Graphics2D)g;
graphics.fill(paddleRect);
graphics.fill(ellipse);
}
#Override
public void actionPerformed(ActionEvent e) {
x = x + xx;
y = y + yy;
if(x<0){
x=0;
xx=0;
}
repaint();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
if(c==KeyEvent.VK_RIGHT){
xx=1;
}else if(c==KeyEvent.VK_LEFT){
xx=-1;
}
}
#Override
public void keyReleased(KeyEvent e) {
xx=0;
}
protected JPanel makePanel() {
#SuppressWarnings("serial")
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
};
pane.setBackground(Color.CYAN);
return pane;
}
protected class timeBall implements ActionListener{
Timer timeWhateva = new Timer(100, this);
#Override
public void actionPerformed(ActionEvent e) {
try{
System.out.println(paddleRect.getX());
if(ellipse.intersects(paddleRect)){
timeWhateva.start();
Oy+=-1;
System.out.println(ellipse.getX() + " " + ellipse.getY());
}else if(!ellipse.intersects(paddleRect)){
Oyy+=1;
}
}catch(RuntimeException NullPointerException){
System.out.println(NullPointerException.getMessage());
}
repaint();
}
}
public static void main(String[] args){
nonaMaingamePractice main = new nonaMaingamePractice();
JFrame frame = new JFrame();
frame.add(main);
frame.setVisible(true);
frame.setTitle("Project 4 game");
frame.setSize(screenWidth, screenHeight);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Okay, so there seems to a few things that are wrong.
First, don't rely on static for cross object communication, this is a really bad idea which will come back to bite you hard. Instead, pass information to the classes which need it.
Second, I'd focus on having a single Timer (or "main-loop") which is responsible for updating the current state of the game and scheduling repaints. This is the basic concept of Model-View-Controller paradigm
The first thing I'm going to do is take your code apart completely and rebuild it...
To start with, I want some kind of interface which provides information about the current state of the game and which I can pass instances of to other parts of the game in order for them to make decisions and update the state of the game...
public interface GameView {
public boolean isKeyRightPressed();
public boolean isKeyLeftPressed();
public Dimension getSize();
public void updateState();
}
This provides information about the state of the right and left keys, the size of the view and provides some basic functionality to request that the view update it's current state
Next, we need some way to model the state of the game...
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
public interface GameModel {
public Rectangle getPaddle();
public Ellipse2D getBall();
public void ballWasMissed();
}
So, this basically maintains information about the paddle and ball and provides a means by which the "main game loop" can provide notification about the state of the game back to the model
Next, we need to the actual "main game loop" or controller. This is responsible for updating the state of the model and updating the view...
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
public class MainLoop implements ActionListener {
private GameView gameView;
private GameModel gameModel;
private int ballYDelta = 1;
public MainLoop(GameView gameView, GameModel gameModel) {
this.gameView = gameView;
this.gameModel = gameModel;
}
#Override
public void actionPerformed(ActionEvent e) {
Rectangle paddle = gameModel.getPaddle();
Ellipse2D ball = gameModel.getBall();
// Update the paddle position...
if (gameView.isKeyLeftPressed()) {
paddle.x--;
} else if (gameView.isKeyRightPressed()) {
paddle.x++;
}
// Correct for overflow...
if (paddle.x < 0) {
paddle.x = 0;
} else if (paddle.x + paddle.width > gameView.getSize().width) {
paddle.x = gameView.getSize().width - paddle.width;
}
// Update the ball position...
Rectangle bounds = ball.getBounds();
bounds.y += ballYDelta;
if (bounds.y < 0) {
bounds.y = 0;
ballYDelta *= -1;
} else if (bounds.y > gameView.getSize().height) {
// Ball is out of bounds...
// Notify the gameView so it knows what to do when the ball goes
// out of the game view's viewable, ie update the score...
// Reset ball position to just out side the top of the view...
gameModel.ballWasMissed();
bounds.y = -bounds.height;
} else if (paddle.intersects(bounds)) {
// Put the ball to the top of the paddle
bounds.y = paddle.y - bounds.height;
// Bounce
ballYDelta *= -1;
}
ball.setFrame(bounds);
// Update the view
gameView.updateState();
}
}
This is basically where we are making decisions about the current position of the objects and updating their positions. Here we check for "out-of-bounds" positions and update their states appropriately (for example, the ball can "bounce" and change directions)
The delta values are quite small, so you might want to play around with those
And finally, we need something that pulls it all together...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class NonaMaingamePractice extends JPanel implements KeyListener, GameView {
/**
*
*/
private static final long serialVersionUID = 1L;
JLabel text = new JLabel("cool");
private Timer timeBall;
private GameModel model;
private boolean init = false;
private boolean rightIsPressed;
private boolean leftIsPressed;
public NonaMaingamePractice() {
setLayout(new BorderLayout());
addKeyListener(this);
setFocusable(true);
JPanel panelNorth = makePanel();
panelNorth.setBackground(Color.CYAN);
add(panelNorth, BorderLayout.NORTH);
JLabel scoreLabel = new JLabel("Score: " + 0);
panelNorth.add(scoreLabel);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if (getWidth() > 0 && getHeight() > 0 && !init) {
init = true;
model = new DefaultGameModel(getSize());
timeBall = new Timer(40, new MainLoop(NonaMaingamePractice.this, model));
timeBall.start();
} else if (model != null) {
model.getPaddle().y = (getHeight() - model.getPaddle().height) - 10;
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
Graphics2D graphics = (Graphics2D) g;
if (model != null) {
graphics.fill(model.getPaddle());
graphics.fill(model.getBall());
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_RIGHT) {
rightIsPressed = true;
} else if (c == KeyEvent.VK_LEFT) {
leftIsPressed = true;
}
}
#Override
public void keyReleased(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_RIGHT) {
rightIsPressed = false;
} else if (c == KeyEvent.VK_LEFT) {
leftIsPressed = false;
}
}
protected JPanel makePanel() {
#SuppressWarnings("serial")
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
};
pane.setBackground(Color.CYAN);
return pane;
}
#Override
public boolean isKeyRightPressed() {
return rightIsPressed;
}
#Override
public boolean isKeyLeftPressed() {
return leftIsPressed;
}
#Override
public void updateState() {
// Maybe update the score??
repaint();
}
public static void main(String[] args) {
NonaMaingamePractice main = new NonaMaingamePractice();
JFrame frame = new JFrame();
frame.add(main);
frame.setVisible(true);
frame.setTitle("Project 4 game");
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Scroll JScrollPane by dragging mouse (Java swing)

I am making a map editor for a game I am working on. There is a JPanel in the JScrollPane that displays the map to be edited. What I would like to do is make it that when the user is holding down the Spacebar and dragging their mouse in the JPanel, the JScrollPanel will scroll along with the dragging. Here is what I have so far:
panelMapPanel.addMouseMotionListener(new MouseMotionListener(){
#Override
public void mouseDragged(MouseEvent e) {
//Gets difference in distance x and y from last time this listener was called
int deltaX = mouseX - e.getX();
int deltaY = mouseY - e.getY();
mouseX = e.getX();
mouseY = e.getY();
if(spacePressed){
//Scroll the scrollpane according to the distance travelled
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getValue() + deltaY);
scrollPane.getHorizontalScrollBar().setValue(scrollPane.getHorizontalScrollBar().getValue() + deltaX);
}
}
});
Currently it works but the scrolling is not smooth at all. Moving the mouse a lot at a time is fine but doing small drags makes the scrollpane go berserk.
Any ideas how to improve this?
For those who enjoy a visual to help, here is the editor:
Addition Notes (Edit):
I have tried scrollPane.getViewport().setViewPosition(new Point(scrollPane.getViewport().getViewPosition().x + deltaX, scrollPane.getViewport().getViewPosition().y + deltaY));
The dragging is more fidgety when moving the mouse slowly, while big movements are more smooth
I tried using scrollRectToVisible without luck
Okay, that ended up been much simpler then I though it would be...
First, don't mess with the JViewport, instead, use JComponent#scrollRectToVisible directly on the component which is acting as the contents of the JScrollPane, onto which the MouseListener should be attached.
The following example simply calculates the difference between the point at which the user clicked and the amount they have dragged. It then applies this delta to the JViewport's viewRect and uses JComponent#scrollRectToVisible to update the viewable area, simple :)
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel map;
public TestPane() {
setLayout(new BorderLayout());
try {
map = new JLabel(new ImageIcon(ImageIO.read(new File("c:/treasuremap.jpg"))));
map.setAutoscrolls(true);
add(new JScrollPane(map));
MouseAdapter ma = new MouseAdapter() {
private Point origin;
#Override
public void mousePressed(MouseEvent e) {
origin = new Point(e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
if (origin != null) {
JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, map);
if (viewPort != null) {
int deltaX = origin.x - e.getX();
int deltaY = origin.y - e.getY();
Rectangle view = viewPort.getViewRect();
view.x += deltaX;
view.y += deltaY;
map.scrollRectToVisible(view);
}
}
}
};
map.addMouseListener(ma);
map.addMouseMotionListener(ma);
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I found this (very common) requirement surprisingly hard to solve. This is the stable solution we have had in production for probably over 10 years.
The accepted answer seems very tempting, but has usability glitches once you start to play with it (e.g. try to immediately drag to the lower right and then back, and you should notice that during the backward movement, no moving takes places for a long time).
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.border.MatteBorder;
import javax.swing.event.MouseInputAdapter;
public class Mover extends MouseInputAdapter {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 160);
f.setLocationRelativeTo(null);
f.setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane();
f.add(scrollPane, BorderLayout.CENTER);
JPanel view = new JPanel();
view.add(new JLabel("Some text"));
view.setBorder(new MatteBorder(5, 5, 5, 5, Color.BLUE));
view.setBackground(Color.WHITE);
view.setPreferredSize(new Dimension(230, 200));
new Mover(view);
scrollPane.setViewportView(view);
f.setVisible(true);
}
private JComponent m_view = null;
private Point m_holdPointOnView = null;
public Mover(JComponent view) {
m_view = view;
m_view.addMouseListener(this);
m_view.addMouseMotionListener(this);
}
#Override
public void mousePressed(MouseEvent e) {
m_view.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
m_holdPointOnView = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
m_view.setCursor(null);
}
#Override
public void mouseDragged(MouseEvent e) {
Point dragEventPoint = e.getPoint();
JViewport viewport = (JViewport) m_view.getParent();
Point viewPos = viewport.getViewPosition();
int maxViewPosX = m_view.getWidth() - viewport.getWidth();
int maxViewPosY = m_view.getHeight() - viewport.getHeight();
if(m_view.getWidth() > viewport.getWidth()) {
viewPos.x -= dragEventPoint.x - m_holdPointOnView.x;
if(viewPos.x < 0) {
viewPos.x = 0;
m_holdPointOnView.x = dragEventPoint.x;
}
if(viewPos.x > maxViewPosX) {
viewPos.x = maxViewPosX;
m_holdPointOnView.x = dragEventPoint.x;
}
}
if(m_view.getHeight() > viewport.getHeight()) {
viewPos.y -= dragEventPoint.y - m_holdPointOnView.y;
if(viewPos.y < 0) {
viewPos.y = 0;
m_holdPointOnView.y = dragEventPoint.y;
}
if(viewPos.y > maxViewPosY) {
viewPos.y = maxViewPosY;
m_holdPointOnView.y = dragEventPoint.y;
}
}
viewport.setViewPosition(viewPos);
}
}
I'm currently working on a map editor myself. I have gotten mouse scrolling to work smoothly on mine although it is a pretty verbose solution.
I wrote two custom AWTEventListeners one for mouse events the other for mouse move events. I did this because my map is a custom JComponent and as such does not fill the entire view-port. This means that scroll pane mouse events wont be detected if the cursor is over the component.
For me this works very smoothly, the content scrolls in perfect lock-step with the mouse cursor.
(I should mention I use the mouse wheel click and not the space bar but it's easy to change).
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if(event instanceof MouseEvent){
MouseEvent e = (MouseEvent)event;
//Begin a scroll if mouse is clicked on our pane
if(isMouseInMapPane()){
if(e.getID() == MouseEvent.MOUSE_PRESSED){
if(e.getButton() == MouseEvent.BUTTON2){
mouseWheelDown = true;
currentX = MouseInfo.getPointerInfo().getLocation().x;
currentY = MouseInfo.getPointerInfo().getLocation().y;
}
}
}
//Stop the scroll if mouse is released ANYWHERE
if(e.getID() == MouseEvent.MOUSE_RELEASED){
if(e.getButton() == MouseEvent.BUTTON2){
mouseWheelDown = false;
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if(event instanceof MouseEvent){
MouseEvent e = (MouseEvent)event;
//Update the scroll based on delta drag value
if(e.getID() == MouseEvent.MOUSE_DRAGGED){
if(mouseWheelDown){
int newX = MouseInfo.getPointerInfo().getLocation().x;
int newY = MouseInfo.getPointerInfo().getLocation().y;
int scrollStepX = (currentX - newX);
int scrollStepY = (currentY - newY);
currentX = newX;
currentY = newY;
//mapScroll is the reference to JScrollPane
int originalValX = mapScroll.getHorizontalScrollBar().getValue();
mapScroll.getHorizontalScrollBar().setValue(originalValX + scrollStepX);
int originalValY = mapScroll.getVerticalScrollBar().getValue();
mapScroll.getVerticalScrollBar().setValue(originalValY + scrollStepY);
}
}
}
}
}, AWTEvent.MOUSE_MOTION_EVENT_MASK);
This is the isMouseInPane method:
private boolean isMouseInMapPane(){
//Note: mapPane does not need to be your scroll pane.
//it can be an encapsulating container as long as it is in
//the same position and the same width/height as your scrollPane.
//For me I used the JPanel containing my scroll pane.
Rectangle paneBounds = mapPane.getBounds();
paneBounds.setLocation(mapPane.getLocationOnScreen());
boolean inside = paneBounds.contains(MouseInfo.getPointerInfo().getLocation());
return inside;
}
This code can be placed anywhere that you have access to your scroll pane reference or you could create a custom scroll pane class and add it there.
I hope it helps!
I've come up to solution as below (the method above didn't work for me, JDK 1.8):
Attach the MouseDragged event to your JScrollPane;
The event function is fired twice, on the start and at the end of the drag;
You'll need a global variables to store initial mouse pointer position (xS and yS);
Here's the code for your MouseDragged:
yourJScrollPaneMouseDragged(java.awt.event.MouseEvent evt) {
Rectangle view = yourJScrollPane.getVisibleRect();
if (xS == 0 && yS == 0) { // first time event fired, store the initial mouse position
xS = evt.getX();
yS = evt.getY();
} else { // second time event fired - actual scrolling
int speed = 20;
view.x += Integer.signum(xS - evt.getX()) * speed;
view.y += Integer.signum(yS - evt.getY()) * speed;
// The view is scrolled by constant value of 20.
// For some reason, periodically, second position values were off for me by alot,
// which caused unwanted jumps.
// Integer.signum gets the direction the movement was performed.
// You can ommit the signum and constant and
// check if it works for you without jagging.
yourJScrollPane.getViewport().scrollRectToVisible(view);
// you actually have to fire scrollRectToVisible with the child
// component within JScrollPane, Viewport is the top child
// reset globals:
xS = 0;
yS = 0;
}
}

Close InternalJFrame without selected the frame

I am really new for Java. I have question about getting the JInternalFrame . I searched the web and find the example. I modified the example a little bit, adding a close menuitem but it didn't work my code. In the project if I closed the JInternalFrame without select it first, then I have error. I tried to loop through the WindowMenu for getting the selected JcheckboxMenuitem, but it didn't get any components. Would someone tell me what to do.
There is the code:
// This is example is from Kjell Dirdal.
// Referenced from http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-mdi.html
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyVetoException;
import javax.swing.DefaultDesktopManager;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
public class KjellDirdalNotepad extends JFrame {
private MDIDesktopPane desktop = new MDIDesktopPane();
private JMenuBar menuBar = new JMenuBar();
private JMenu fileMenu = new JMenu("File");
private JMenuItem newMenu = new JMenuItem("New");
private JScrollPane scrollPane = new JScrollPane();
private JMenuItem closeMenu=new JMenuItem("Close");
private int index=1;
private WindowMenu wMenu=null;
public KjellDirdalNotepad() {
menuBar.add(fileMenu);
wMenu=new WindowMenu(desktop);
menuBar.add(wMenu);
fileMenu.add(newMenu);
fileMenu.add(closeMenu);
setJMenuBar(menuBar);
setTitle("MDI Test");
scrollPane.getViewport().add(desktop);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(scrollPane, BorderLayout.CENTER);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
newMenu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
desktop.add(new TextFrame(String.valueOf(index)));
index=index+1;
}
});
//I added the close menu
closeMenu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JInternalFrame f=desktop.getSelectedFrame();
if (f==null)
{
for (Component child: wMenu.getComponents()){
if (child instanceof WindowMenu.ChildMenuItem){
JCheckBoxMenuItem item=(JCheckBoxMenuItem) child;
if( item.isSelected()){
DisplayTestMsg(item.getText());
}
}
}
}
else{
f.dispose();
}
}
});
}
public void DisplayTestMsg(String msg){
//custom title, error icon
JTextArea textArea=new JTextArea(msg);
textArea.setColumns(30);
textArea.setLineWrap( true );
textArea.setWrapStyleWord( true );
textArea.setSize(textArea.getPreferredSize().width, 1);
JOptionPane.showMessageDialog(null,
textArea,
"Test",
JOptionPane.WARNING_MESSAGE);
}
public static void main(String[] args) {
KjellDirdalNotepad notepad = new KjellDirdalNotepad();
notepad.setSize(600, 400);
notepad.setVisible(true);
}
}
class TextFrame extends JInternalFrame {
private JTextArea textArea = new JTextArea();
private JScrollPane scrollPane = new JScrollPane();
public TextFrame(String title) {
setSize(200, 300);
setTitle("Edit Text-" + title);
setMaximizable(true);
setIconifiable(true);
setClosable(true);
setResizable(true);
scrollPane.getViewport().add(textArea);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(scrollPane, BorderLayout.CENTER);
}
}
/**
* An extension of WDesktopPane that supports often used MDI functionality. This
* class also handles setting scroll bars for when windows move too far to the
* left or bottom, providing the MDIDesktopPane is in a ScrollPane.
*/
class MDIDesktopPane extends JDesktopPane {
private static int FRAME_OFFSET = 20;
private MDIDesktopManager manager;
public MDIDesktopPane() {
manager = new MDIDesktopManager(this);
setDesktopManager(manager);
setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
}
public void setBounds(int x, int y, int w, int h) {
super.setBounds(x, y, w, h);
checkDesktopSize();
}
public Component add(JInternalFrame frame) {
JInternalFrame[] array = getAllFrames();
Point p;
int w;
int h;
Component retval = super.add(frame);
checkDesktopSize();
if (array.length > 0) {
p = array[0].getLocation();
p.x = p.x + FRAME_OFFSET;
p.y = p.y + FRAME_OFFSET;
} else {
p = new Point(0, 0);
}
frame.setLocation(p.x, p.y);
if (frame.isResizable()) {
w = getWidth() - (getWidth() / 3);
h = getHeight() - (getHeight() / 3);
if (w < frame.getMinimumSize().getWidth())
w = (int) frame.getMinimumSize().getWidth();
if (h < frame.getMinimumSize().getHeight())
h = (int) frame.getMinimumSize().getHeight();
frame.setSize(w, h);
}
moveToFront(frame);
frame.setVisible(true);
try {
frame.setSelected(true);
} catch (PropertyVetoException e) {
frame.toBack();
}
return retval;
}
public void remove(Component c) {
super.remove(c);
checkDesktopSize();
}
/**
* Cascade all internal frames
*/
public void cascadeFrames() {
int x = 0;
int y = 0;
JInternalFrame allFrames[] = getAllFrames();
manager.setNormalSize();
int frameHeight = (getBounds().height - 5) - allFrames.length * FRAME_OFFSET;
int frameWidth = (getBounds().width - 5) - allFrames.length * FRAME_OFFSET;
for (int i = allFrames.length - 1; i >= 0; i--) {
allFrames[i].setSize(frameWidth, frameHeight);
allFrames[i].setLocation(x, y);
x = x + FRAME_OFFSET;
y = y + FRAME_OFFSET;
}
}
/**
* Tile all internal frames
*/
public void tileFrames() {
java.awt.Component allFrames[] = getAllFrames();
manager.setNormalSize();
int frameHeight = getBounds().height / allFrames.length;
int y = 0;
for (int i = 0; i < allFrames.length; i++) {
allFrames[i].setSize(getBounds().width, frameHeight);
allFrames[i].setLocation(0, y);
y = y + frameHeight;
}
}
/**
* Sets all component size properties ( maximum, minimum, preferred) to the
* given dimension.
*/
public void setAllSize(Dimension d) {
setMinimumSize(d);
setMaximumSize(d);
setPreferredSize(d);
}
/**
* Sets all component size properties ( maximum, minimum, preferred) to the
* given width and height.
*/
public void setAllSize(int width, int height) {
setAllSize(new Dimension(width, height));
}
private void checkDesktopSize() {
if (getParent() != null && isVisible())
manager.resizeDesktop();
}
}
/**
* Private class used to replace the standard DesktopManager for JDesktopPane.
* Used to provide scrollbar functionality.
*/
class MDIDesktopManager extends DefaultDesktopManager {
private MDIDesktopPane desktop;
public MDIDesktopManager(MDIDesktopPane desktop) {
this.desktop = desktop;
}
public void endResizingFrame(JComponent f) {
super.endResizingFrame(f);
resizeDesktop();
}
public void endDraggingFrame(JComponent f) {
super.endDraggingFrame(f);
resizeDesktop();
}
public void setNormalSize() {
JScrollPane scrollPane = getScrollPane();
int x = 0;
int y = 0;
Insets scrollInsets = getScrollPaneInsets();
if (scrollPane != null) {
Dimension d = scrollPane.getVisibleRect().getSize();
if (scrollPane.getBorder() != null) {
d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, d.getHeight()
- scrollInsets.top - scrollInsets.bottom);
}
d.setSize(d.getWidth() - 20, d.getHeight() - 20);
desktop.setAllSize(x, y);
scrollPane.invalidate();
scrollPane.validate();
}
}
private Insets getScrollPaneInsets() {
JScrollPane scrollPane = getScrollPane();
if (scrollPane == null)
return new Insets(0, 0, 0, 0);
else
return getScrollPane().getBorder().getBorderInsets(scrollPane);
}
private JScrollPane getScrollPane() {
if (desktop.getParent() instanceof JViewport) {
JViewport viewPort = (JViewport) desktop.getParent();
if (viewPort.getParent() instanceof JScrollPane)
return (JScrollPane) viewPort.getParent();
}
return null;
}
protected void resizeDesktop() {
int x = 0;
int y = 0;
JScrollPane scrollPane = getScrollPane();
Insets scrollInsets = getScrollPaneInsets();
if (scrollPane != null) {
JInternalFrame allFrames[] = desktop.getAllFrames();
for (int i = 0; i < allFrames.length; i++) {
if (allFrames[i].getX() + allFrames[i].getWidth() > x) {
x = allFrames[i].getX() + allFrames[i].getWidth();
}
if (allFrames[i].getY() + allFrames[i].getHeight() > y) {
y = allFrames[i].getY() + allFrames[i].getHeight();
}
}
Dimension d = scrollPane.getVisibleRect().getSize();
if (scrollPane.getBorder() != null) {
d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, d.getHeight()
- scrollInsets.top - scrollInsets.bottom);
}
if (x <= d.getWidth())
x = ((int) d.getWidth()) - 20;
if (y <= d.getHeight())
y = ((int) d.getHeight()) - 20;
desktop.setAllSize(x, y);
scrollPane.invalidate();
scrollPane.validate();
}
}
}
/**
* Menu component that handles the functionality expected of a standard
* "Windows" menu for MDI applications.
*/
class WindowMenu extends JMenu {
private MDIDesktopPane desktop;
private JMenuItem cascade = new JMenuItem("Cascade");
private JMenuItem tile = new JMenuItem("Tile");
public WindowMenu(MDIDesktopPane desktop) {
this.desktop = desktop;
setText("Window");
cascade.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
WindowMenu.this.desktop.cascadeFrames();
}
});
tile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
WindowMenu.this.desktop.tileFrames();
}
});
addMenuListener(new MenuListener() {
public void menuCanceled(MenuEvent e) {
}
public void menuDeselected(MenuEvent e) {
removeAll();
}
public void menuSelected(MenuEvent e) {
buildChildMenus();
}
});
}
/* Sets up the children menus depending on the current desktop state */
private void buildChildMenus() {
int i;
ChildMenuItem menu;
JInternalFrame[] array = desktop.getAllFrames();
add(cascade);
add(tile);
if (array.length > 0)
addSeparator();
cascade.setEnabled(array.length > 0);
tile.setEnabled(array.length > 0);
for (i = 0; i < array.length; i++) {
menu = new ChildMenuItem(array[i]);
menu.setState(i == 0);
menu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JInternalFrame frame = ((ChildMenuItem) ae.getSource()).getFrame();
frame.moveToFront();
try {
frame.setSelected(true);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
});
menu.setIcon(array[i].getFrameIcon());
add(menu);
}
}
/*
* This JCheckBoxMenuItem descendant is used to track the child frame that
* corresponds to a give menu.
*/
class ChildMenuItem extends JCheckBoxMenuItem {
private JInternalFrame frame;
public ChildMenuItem(JInternalFrame frame) {
super(frame.getTitle());
this.frame = frame;
}
public JInternalFrame getFrame() {
return frame;
}
}
}
If you want to remove the JPanel when a button is pressed, you can call JFrame.remove(panel) such as:
final JFrame frame = new JFrame("test");
final JPanel panel = new JPanel();
//add the panel at the start
frame.add(panel);
// when the buttons clicked
button.addActionListener(new ActionListener() {
#Override
public void ActionPerformed(ActionEvent e) {
frame.remove(panel);
frame.repaint();
};
});
I figured it out. I added the method on Class WindowMenu as belows. This method is called when the close button is click without selected internal frame.
public JInternalFrame getFrontFrame(){
InternalFrame[] array = desktop.getAllFrames();
JInternalFrame f=(JInternalFrame)array[0];
return f;
}

How to remove gap in java swing label of large size

in my application I have label which has a font size over 200. This label contains big up and down (irregular)gap. How can I remove it ?
This is my code:
package Core;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class LabelDemo extends JPanel {
public LabelDemo() {
super(new GridBagLayout());
JLabel label2;
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
// Create the other labels.
label2 = new JLabel("Text-Only Label");
label2.setBorder(BorderFactory.createTitledBorder("aaaaaaaa"));
label2.setFont(new Font("Verdana", Font.PLAIN, (int) 220));
// label2.setBorder(new EmptyBorder(-50, 0, 0, 0));
// Add the labels.
add(label2, c);
}
/**
* Create the GUI and show it. For thread safety, this method should be invoked from the event dispatch thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("LabelDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add content to the window.
frame.add(new LabelDemo());
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event dispatch thread:
// creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
I also try my last post: How to change gap in swing label and experiment with insets but this looks different in linux and windows
Is there some better way how to remove this gap ?
JDigit may give you some ideas:
It override's paintComponent() to down-sample a high-resolution BufferedImage and control the geometry.
It uses setBorderPainted(false) to set the borderPainted property.
It uses a FocusHandler for custom highlighting.
Addendum: As noted here, the underlying problem is the font's leading, defined in FontMetrics as being included in the font's height. As suggested in a comment by #Guillaume Polet, you can render the text wherever you want in your own JComponent. TextLayout, discussed here, can be used to calculate the bounds, as shown below.
Pros:
Absolute control over placement.
Geometry of TexteLayout bounds based on FontMetrics.
Cons:
No Icon support.
No HTML support.
Note that the JComponent authors "recommend that you put the component in a JPanel and set the border on the JPanel."
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/16014525/230513
*/
public class UnleadedTest {
private static class Unleaded extends JComponent {
private Font font = new Font("Verdana", Font.PLAIN, 144);
private FontRenderContext frc = new FontRenderContext(null, true, true);
private String text;
private TextLayout layout;
private Rectangle r;
public Unleaded(String text) {
this.text = text;
calcBounds();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(r.width, r.height);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
calcBounds();
layout.draw(g2d, -r.x, -r.y);
}
private void calcBounds() {
layout = new TextLayout(text, font, frc);
r = layout.getPixelBounds(null, 0, 0);
}
}
private void display() {
JFrame f = new JFrame("Unleaded");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Unleaded label = new Unleaded("Unleaded");
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("Title"));
panel.add(label);
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new UnleadedTest().display();
}
});
}
}
The "right way" to do this would be to extend "BasicLabelUI" and override the "protected String layoutCL()" method. This is the method that is responsible for laying out everything inside the label and is called when the "getPreferredSize()" of the JLabel is called. So this method determines the height the component is going to be.
If you drill down deep enough you'll see that the height is determined by the following line in the SwingUtilities:1021 class (which is used by layoutCL):
textR.height = fm.getHeight();
So the label is not causing the white space, the font is. The label just conforms to what the FontMetrics object says is the maximum height of the font for that size.
The easiest way would probably be to cheat; Force the size calculation to do something it shouldn't. Below is your example with a custom LabelUI component which you can experiment on. For example if you force the variable to 'dy' to '-40' the text will be at the top. If you want to make something more durable you could check all the leters in the string of the label, measure their maximum height and use that in the layoutCL method. But thats more work obviously.
package Core;
import sun.swing.SwingUtilities2;
import javax.swing.*;
import javax.swing.plaf.LabelUI;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.text.View;
import java.awt.*;
public class LabelDemo extends JPanel {
public LabelDemo() {
super(new GridBagLayout());
JLabel label2;
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
// Create the other labels.
label2 = new JLabel("Text-Only Label");
label2.setVerticalAlignment(SwingUtilities.TOP);
label2.setVerticalTextPosition(SwingUtilities.TOP);
label2.setUI(SkinnyLabelUI.createUI(label2));
label2.setBorder(BorderFactory.createTitledBorder("aaaaaaaa"));
label2.setFont(new Font("Verdana", Font.PLAIN, (int) 220));
// label2.setBorder(new EmptyBorder(-50, 0, 0, 0));
// Add the labels.
add(label2, c);
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event dispatch thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("LabelDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add content to the window.
frame.add(new LabelDemo());
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event dispatch thread:
// creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
private static class SkinnyLabelUI extends BasicLabelUI {
private static final SkinnyLabelUI labelUI = new SkinnyLabelUI();
public static LabelUI createUI(JComponent c) {
return labelUI;
}
protected String layoutCL(
JLabel label,
FontMetrics fm,
String text,
Icon icon,
Rectangle viewR,
Rectangle iconR,
Rectangle textR) {
int verticalAlignment = label.getVerticalAlignment();
int horizontalAlignment = label.getHorizontalAlignment();
int verticalTextPosition = label.getVerticalTextPosition();
int horizontalTextPosition = label.getHorizontalTextPosition();
if (icon != null) {
iconR.width = icon.getIconWidth();
iconR.height = icon.getIconHeight();
} else {
iconR.width = iconR.height = 0;
}
/* Initialize the text bounds rectangle textR. If a null
* or and empty String was specified we substitute "" here
* and use 0,0,0,0 for textR.
*/
boolean textIsEmpty = (text == null) || text.equals("");
int lsb = 0;
int rsb = 0;
/* Unless both text and icon are non-null, we effectively ignore
* the value of textIconGap.
*/
int gap;
View v;
if (textIsEmpty) {
textR.width = textR.height = 0;
text = "";
gap = 0;
} else {
int availTextWidth;
gap = (icon == null) ? 0 : label.getIconTextGap();
if (horizontalTextPosition == SwingUtilities.CENTER) {
availTextWidth = viewR.width;
} else {
availTextWidth = viewR.width - (iconR.width + gap);
}
v = (label != null) ? (View) label.getClientProperty("html") : null;
if (v != null) {
textR.width = Math.min(availTextWidth,
(int) v.getPreferredSpan(View.X_AXIS));
textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
} else {
textR.width = SwingUtilities2.stringWidth(label, fm, text);
lsb = SwingUtilities2.getLeftSideBearing(label, fm, text);
if (lsb < 0) {
// If lsb is negative, add it to the width and later
// adjust the x location. This gives more space than is
// actually needed.
// This is done like this for two reasons:
// 1. If we set the width to the actual bounds all
// callers would have to account for negative lsb
// (pref size calculations ONLY look at width of
// textR)
// 2. You can do a drawString at the returned location
// and the text won't be clipped.
textR.width -= lsb;
}
if (textR.width > availTextWidth) {
text = SwingUtilities2.clipString(label, fm, text,
availTextWidth);
textR.width = SwingUtilities2.stringWidth(label, fm, text);
}
textR.height = fm.getHeight();
System.out.println("font height: " + textR.height);
}
}
/* Compute textR.x,y given the verticalTextPosition and
* horizontalTextPosition properties
*/
if (verticalTextPosition == SwingUtilities.TOP) {
if (horizontalTextPosition != SwingUtilities.CENTER) {
textR.y = 0;
} else {
textR.y = -(textR.height + gap);
}
} else if (verticalTextPosition == SwingUtilities.CENTER) {
textR.y = (iconR.height / 2) - (textR.height / 2);
} else { // (verticalTextPosition == BOTTOM)
if (horizontalTextPosition != SwingUtilities.CENTER) {
textR.y = iconR.height - textR.height;
} else {
textR.y = (iconR.height + gap);
}
}
if (horizontalTextPosition == SwingUtilities.LEFT) {
textR.x = -(textR.width + gap);
} else if (horizontalTextPosition == SwingUtilities.CENTER) {
textR.x = (iconR.width / 2) - (textR.width / 2);
} else { // (horizontalTextPosition == RIGHT)
textR.x = (iconR.width + gap);
}
// WARNING: DefaultTreeCellEditor uses a shortened version of
// this algorithm to position it's Icon. If you change how this
// is calculated, be sure and update DefaultTreeCellEditor too.
/* labelR is the rectangle that contains iconR and textR.
* Move it to its proper position given the labelAlignment
* properties.
*
* To avoid actually allocating a Rectangle, Rectangle.union
* has been inlined below.
*/
int labelR_x = Math.min(iconR.x, textR.x);
int labelR_width = Math.max(iconR.x + iconR.width,
textR.x + textR.width) - labelR_x;
int labelR_y = Math.min(iconR.y, textR.y);
int labelR_height = Math.max(iconR.y + iconR.height,
textR.y + textR.height) - labelR_y;
int dx, dy;
if (verticalAlignment == SwingUtilities.TOP) {
dy = viewR.y - labelR_y;
} else if (verticalAlignment == SwingUtilities.CENTER) {
dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2));
} else { // (verticalAlignment == BOTTOM)
dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
}
if (horizontalAlignment == SwingUtilities.LEFT) {
dx = viewR.x - labelR_x;
} else if (horizontalAlignment == SwingUtilities.RIGHT) {
dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
} else { // (horizontalAlignment == CENTER)
dx = (viewR.x + (viewR.width / 2))
- (labelR_x + (labelR_width / 2));
}
/* Translate textR and glypyR by dx,dy.
*/
textR.x += dx;
textR.y += dy;
iconR.x += dx;
iconR.y += dy;
if (lsb < 0) {
// lsb is negative. Shift the x location so that the text is
// visually drawn at the right location.
textR.x -= lsb;
textR.width += lsb;
}
if (rsb > 0) {
textR.width -= rsb;
}
return text;
}
}
}
changing the border offset might help:
int OFFSET_TOP=50,OFFSET_BOTTOM=50;
label.setBorder(new TitledBorder(TITLE){
#Override
public Insets getBorderInsets(Component c, Insets insets){
return new Insets(insets.top - OFFSET_TOP, insets.left, insets.bottom - OFFSET_BOTTOM, insets.right);
}
});

Categories

Resources