How to lock JSplitPane divider when swapping components? - java

I have a simple extended JSplitPane that I set different panels to at different times when they are needed. Specifically, I split it into an upper and lower section, and I swap out the bottom section frequently. Each time I do, I reset the slider position to how I want it, but sometimes it jumps off at and re-positions itself to the top of the screen (not always).
Here's my code:
public class MainPanel extends JSplitPane{
public Screen screen;
public int height;
public ControlPanel curPanel;
public MainPanel(Screen screen, int height){
super(JSplitPane.VERTICAL_SPLIT);
this.screen = screen;
this.height = height;
setDividerSize(2);
setEnabled(false);
setTopComponent(screen);
setToInitControls();
}
public void setToInitControls(){
InitControls initCtrls = new InitControls(this);
setBottomComponent(initCtrls);
curPanel = initCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToConfigControls(){
ConfigControls configCtrls = new ConfigControls(this);
setBottomComponent(configCtrls);
curPanel = configCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToWaitControls(){
WaitControls waitCtrls = new WaitControls(this);
setBottomComponent(null);
setBottomComponent(waitCtrls);
curPanel = waitCtrls;
setDividerLocation(height / 4 * 3);
}
//and so on (I have more methods like these further down)
//OVERRIDES: I figured overriding these might help. It didn't.
#Override
public int getMinimumDividerLocation(){
return (height / 4 * 3);
}
#Override
public int getMaximumDividerLocation(){
return (height / 4 * 3);
}
}
Basically, I use the "setTo...Controls()" methods to swap bottom panels. Is there a way to tell the slider to stay put where I placed it regardless of the panel's preferred sizes, or if not, how do I make the panels know what to shape themselves to fit in? Thanks for any/all suggestions!
EDIT: I should note that these panels do not use layouts. They are custom panels that I use mouse/keyboard listeners on and use my own graphics to paint over them.

I found the solution, thanks to the links above. It's actually quite simple. Instead of using
setDividerLocation(height / 4 * 3);
for every time I added a component, I just replaced it with:
setResizeWeight(0.66);
Did that once inside the constructor, and it never bothered me again. 0.66 is the equivalent decimal position to h/4*3 (I just trial-and-errored it).

Related

How to create a JButton with text on the button left and the icon separated from it on the button right

Basically, I'm trying to make a button that has the text aligned to the left (so I'm using setHorizontalAlignment(SwingConstants.LEFT)) and the image on the right border of the button, far from the text.
I already tried setHorizontalTextAlignment(SwingConstants.LEFT), but that just makes the text go relativity to the left of the icon, which is not exactly what I want, since I needed the icon to be secluded from it.
Also, I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
You can dynamically change the spacing with code like:
JButton button = new JButton("Text on left:")
{
#Override
public void doLayout()
{
super.doLayout();
int preferredWidth = getPreferredSize().width;
int actualWidth = getSize().width;
if (actualWidth != preferredWidth)
{
int gap = getIconTextGap() + actualWidth - preferredWidth;
gap = Math.max(gap, UIManager.getInt("Button.iconTextGap"));
setIconTextGap(gap);
}
}
};
button.setIcon( new ImageIcon("copy16.gif") );
button.setHorizontalTextPosition(SwingConstants.LEADING);
This is a derivative of camickr's answer to allow editing in a GUI builder as well as placing it in a dynamic layout. I also removed the UIManager.getInt("Button.iconTextGap") so the gap will shrink to 0 if necessary.
I called it a 'Justified' button in analogy with justified text alignment (stretches a paragraph to left & right by growing width of space characters).
public class JustifiedButton extends JButton {
#Override
public void doLayout() {
super.doLayout();
setIconTextGap(0);
if (getHorizontalTextPosition() != CENTER) {
int newGap = getSize().width - getMinimumSize().width;
if (newGap > 0)
setIconTextGap(newGap);
}
}
#Override
public Dimension getMinimumSize() {
Dimension minimumSize = super.getMinimumSize();
if (getHorizontalTextPosition() != CENTER)
minimumSize.width -= getIconTextGap();
return minimumSize;
}
#Override
public Dimension getPreferredSize() {
Dimension preferredSize = super.getPreferredSize();
if (getHorizontalTextPosition() != CENTER)
preferredSize.width -= getIconTextGap();
return preferredSize;
}
}
This is not exactly production-ready and needs some field-testing. If I find anything, I'll edit the code.
[edit] Now works for vertical text alignments. Also simplified a bit.
[edit2] Also manipulate getPreferredSize to play nice with scroll pane (otherwise it keeps growing and never shrinks again)
You can add a layout manager to your button.
JButton btn = new JButton();
btn.add(new JLabel(text));
btn.add(new JLabel(img));
btn.setLayout(/*best layout choice here*/);
btn.setPreferredSize(new Dimension(x,y));
btn.setMaximumSize(new Dimension(maxX, minY));
btn.setMinimumSize(new Dimension(minX, minY)); //this one is most important when it comes to layoutmanagers
Sorry I can't be much help when it comes to picking out a good layout - But this will eventually get you what you want. Maybe someone else can comment on which one to use.

Java GridBayLayout replacing components

I am trying to delete multiple components in a JPanel using a GridBagLayout
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
GamePanel gamePanel = new GamePanel(frame.getContentPane());
gamePanel.delBlock(0, 0);
gamePanel.delBlock(0, 1);
frame.setContentPane(gamePanel);
}
});
This is is the method for deleting a block
public void delBlock(int x, int y){
int location = x * row + y;
this.remove(location);
this.revalidate();
this.repaint();
}
As you can see the 2 blocks should be next to one another but this is what i get as a result.
gamePanel.delBlock(0, 0);
gamePanel.delBlock(0, 1);
First you remove component at location 0. Then all the components shift 1 position in the Container.
Then you remove component at location 1. However, this the component that was at location 2, before you removed the first component.
Try:
gamePanel.delBlock(0, 1);
gamePanel.delBlock(0, 0);
to reverse the order in which you remove the components.
That is always remove components from the end of the container first.
As you can see the 2 blocks should be next to one another
I can't tell if you mean "next to" one another in a vertical sense or horizontal sense.
Given that the two components have been removed from the first column, it appears that you are building your grid in column order. That is you add all the components for column 1 and then column 2 and then column 3 etc.
If you want your components to be "beside" one another (in a horizontal sense) then you need to add the components in row order.

Empty JTabbedPane

I'm having an issue creating an empty JTabbedPane where the only portion to be seen on the GUI are the row of tabs.
Everytime I add a new tab with an "empty" component, the height of the JTabbedPane increases, but why?
The current workaround is to override getPreferredSize(), but it seems kludgy to me. Comment out the overridden method to see what I mean.
Am I missing something obvious?
Background:
We need a JTabbedPane where the tabbed pane starts off with 2 tabs, but the user can add more tabs as needed, up to 10. In addition, each tab contains the same components, but with different data. The decision was made to fake the look of a JTabbedPane, by implementing an empty JTabbedPane solely for the look, and to use a single fixed JPanel whose contents will be refreshed based on the tab clicked.
(Normally, I could just recreate the JPanel n-times, but that would nightmarish for the presenter classes who control the UI, which is beyond the scope of my question.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomTabbedPane implements Runnable
{
static final int MAX_TABS = 11; // includes the "add" tab
JPanel pnlTabs;
JTabbedPane tabbedPane;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CustomTabbedPane());
}
public void run()
{
JPanel p = buildPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(p);
frame.setSize(800,400);
frame.setVisible(true);
}
private JPanel buildPanel()
{
tabbedPane = new JTabbedPane()
{
#Override
public Dimension getPreferredSize()
{
Dimension dim = super.getPreferredSize();
dim.height = getUI().getTabBounds(this, 0).height + 1;
return dim;
}
};
tabbedPane.addTab("Tab 1", getEmptyComp());
tabbedPane.addTab("Tab 2", getEmptyComp());
tabbedPane.addTab("+", new TabCreator());
tabbedPane.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
addTab();
}
});
JScrollPane scroll = new JScrollPane(new JTable(5,10));
JPanel p = new JPanel(new BorderLayout());
p.add(tabbedPane, BorderLayout.NORTH);
p.add(scroll, BorderLayout.CENTER);
p.setBorder(BorderFactory.createLineBorder(Color.BLUE.darker(), 1));
return p;
}
private void addTab()
{
if (tabbedPane.getSelectedComponent() instanceof TabCreator)
{
int selIndex = tabbedPane.getSelectedIndex();
if (tabbedPane.getComponentCount() < MAX_TABS)
{
if (selIndex == tabbedPane.getComponentCount()-1)
{
String title = "Tab " + (selIndex + 1);
tabbedPane.insertTab(title, null, getEmptyComp(), "", selIndex);
tabbedPane.setSelectedIndex(selIndex);
if (tabbedPane.getComponentCount() == MAX_TABS)
{
tabbedPane.setEnabledAt(MAX_TABS-1, false);
}
}
}
}
}
private Component getEmptyComp()
{
return Box.createVerticalStrut(1);
}
class TabCreator extends JLabel {}
}
Great question! But it's fairly straightforward to get a hint on what's happening.
The problem is that your content does not have a minimum width, preferred size is not set, tab placement is top/bottom and the UI is default.
Since preferred size is not set, then when the layout is revalidated the calculations of space required go into the BasicTabbedPaneUI method Dimension calculateSize(false).
That reads:
int height = 0;
int width = 0;
<other vars>
// Determine minimum size required to display largest
// child in each dimension
<actual method>
Here it calculates the minimum size to accommodate any child and stores it into height/width. In your case this yields something like 10,10 (because of the single Label tab creator I think, I didn't follow that one).
Then happens the magic:
switch(tabPlacement) {
case LEFT:
case RIGHT:
height = Math.max(height, calculateMaxTabHeight(tabPlacement));
tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
width += tabExtent;
break;
case TOP:
case BOTTOM:
default:
width = Math.max(width, calculateMaxTabWidth(tabPlacement));
tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
height += tabExtent;
}
What happens here is it sets the preferred width to be the maximum of the largest tab width and the largest child width. In your case it's around 44 for the tab text. The tabExtent is then calculated to see just how many rows of tabs are needed to support this preferred width. In your case - it's 1 extra row of tabs for each tab. That's where the extra height in preferredSize().height comes from. Essentially because for horizontal tab placement it cares about width first, then height.
How to fix:
Set a preferred size :) I know a lot of people say don't set the preferred size, but in this case this will just work. Since a preferred size is set (via actually setting it, not overriding getPreferredSize()), the code will never get to counting tabs.
Give at least one of your children a size (via setPreferredSize or overriding getPreferredSize). If one of the childrens width is that of the frame, or, say, the table at the bottom the TabbedPane will not be allocating an extra row for each tab, since a single row will fit everything.
Make your own UI for the tabbed pane. It may be easier to make your own tabbed pane though really, I've never done this.
EDIT:
After thinking about this a bit more, I realized that solution number 1 AND your own solution suffer from the flaw that, if the tabbed pane actually does require multiple rows for the tabs (hello frame resizes), bad things will happen. Don't use it.

How to change string length (calculating its width in pixel) when changing window size and strange behaviour of JLabel

I have JLabel which I would like to change its size while I resize the window. When JLabel contains String which is too big, the String should be shortened, with right part visible and adds dots on the left hand side of the String.
My JLabel is inside innerPanel which is a header in middlePanel which is added to outerPanel. So when I resize window I use listener on outerPanel in that way:
outerPanel.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent evt) {
int width = ((JPanel) evt.getSource()).getWidth();
windowSize = width;
refresh();
}
// [...] other not used override methods
});
refresh() repaints view and creates new middlePanel where is called class which creates innerPanel where is located my JLabel:
Public class InnerPanel extends JPanel {
private int maxSize;
String string = "<VERY_LONG_STRING>";
private static final int DEFAULT_INDEND_PIXEL = 70;
public InnerPanel(int windowSize) {
maxSize = windowSize - DEFAULT_INDENT_PIXEL;
createPanel();
}
private createPanel() {
// [...] gridbag and GridBagConstraints implementation
String shortString = countString();
JLabel label = new JLabel(shortString);
add(label,gc);
}
private String countString() {
int index = 0;
boolean toBig = true;
StringBuilder sb = new StringBuilder(string);
while(toBig) {
Rectangle2d rect = // [...] code which creates rectangle around text from sb.toString()
// I have no access to repo at home but if it's important I can paste it tomorrow
if(rect.getWidth() > maxSize)
sb.deleteCharAt(0);
else
toBig = false;
}
return sb.toString();
}
}
That's works fine in general, bacause it do resize JLabel in one step when I enlarge window in width. But the problem is appear when I try to reduce the window in width. In this case componentResized() calculate width step by step (and it's called multiple times), gradually decreases width by some amount of pixels till it reach real window size. It's behave in that way even thow I change window size in one step from maximum size to 800. Whole process is so slow, that it takes around a second to fit string to window size. So it looks bit like an animation.
The problem is very rare to me, bacause width in componentResized() method is calculeted step by step only when I assign windowSize variable.
When I give windowSize fixed size like for example 500 - componentResized() is called only onces - with correct width indicated real window size (!!) - and there's no its step by step decrease!
It's look like width variable which is assigned by ((JPanel) evt.getSource()).getWidth() knows that windowSize is used to dynamically change size of JLabel component even before first call of refresh() method.
If anyone have an idea what is going on here - I will be very appreciate for help.
You may be able to adapt one of the approaches shown here to better effect. As shown here, the ellipsis is supplied by the label's UI delegate via a call to SwingUtilities2.clipString(), which appends the clipString. Rather than re-invent the label UI, use TextLayout to determine the required geometry, prepend the ellipsis, and handle the alignment in a table or list renderer, as shown here.

editfield weird height

In my application i have 2 types of editfields. One of them behaves like single line editfield, the other behaves like multi-line editfield (editarea). In this screen i have one header, one editfield and one editarea. When i enter some text to editfield, it clips the text and cursor. But, when i enter some text to editarea which includes a tailed character(y,g,q,p) editareas height is changing and editfieldact normal. If i dont enter tailed characters stuation does not change.
Here is my editarea class:
public class EditAreaField extends HorizontalFieldManager{
private net.rim.device.api.ui.component.EditField editArea;
public EditAreaField (){
// some code;
editArea.setPadding(25, 10, 0, 10);
}
public int getPreferredHeight() {
int height = Math.max(editArea.getHeight(), textFont.getHeight());
return height + editArea.getPaddingTop();
}
}
label1 -> editfield
label2 -> editarea
this is because you are making the size to change by using
int height = Math.max(editArea.getHeight(), textFont.getHeight());
instead of this try to give some fixed height. for example
height= Graphics.getScreenHeight()/5;
or you can also use setExtent inside the sublayout method of the manager
protected void sublayout(int maxWidth, int maxHeight)
{
layoutChild(_editField, _editField.getPreferredWidth(), _editField.getPreferredHeight());
setPositionChild(_editField, xpos,ypos);
setExtent(preferredHeight,preferredWidth);
}
I think it will work.
Please let me know
About the cursor painting - you did override drawFocus or/and onFocus or/and onUnfocus and don't repaint properly sometime.

Categories

Resources