Miglayout + hidemode 3 = unwanted autoscroll when showing controls - java

I'm using 'hidemode 3' in MigLayout so that hidden components aren't visible at all (and don't have any impact on layout).
I'm using this so that I can show inline errors underneath textboxes, which show only if there is an error
Whenever I show these inline error boxes (they're text areas so I just call setVisible(true)) the scroll pane the form is embedded within automatically scrolls down to whatever is being set to visible (so I call setVisible(true) on something, it causes a layout change and the scroll pane auto-scrolls downward to where ever the component is)
Now, this isn't the problem - I think I get why the above part happens (presuambly the panel changing size to accomodate the new layout screws up the scale of the scroll bar, so it appears to scroll downward)
What I can't understand is how to work around it - for example I've tried doing this:
// validateModel will cause the setVisible() calls to occur
if (!syncControl_.validateModel())
{
// Here I try and counteract the layout change by going back
// to the top
variableScrollPane_.getViewport().setViewPosition(new Point(0,0));
}
But it doesn't work - or rather, it does work for a moment: The scroll occurs, but the apparent 'auto scroll' then happens a few milliseconds afterwards. It seems whatever redoes the layout either gets called at regular intervals, or setVisible actually fires some kind of event
I've tried calling validate(), invalidate() repaint() etc. prior to the scroll change to no avail.
So I suppose my question is: When I use hidemode 3 and call setVisible() what method is it that does the 'reacting'? Is it during validation / doLayout() etc. or is there something completely separate happening?
Thanks

Just accidentally answered my own question:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
variableScrollPane_.getVerticalScrollBar().setValue(0);
}
});
Turns out MigLayout doesn't factor into this at all (and it seems the above is a well-known mechanism for resetting the scrollbar in many situations)

Related

JXTreeTable scroll upon adding new node?

I have a JXTreeTable with an add button. The button works as expected: it adds a new node to the end, with values that (for the purpose of testing) are hard-coded into my program. The line:
modelSupport.fireChildAdded(new TreePath(root), noOfChildren-1, dataNode);
tells the JXTreeTable to recognise the update made to the TreeTableModel, and to refresh accordingly.
I then execute:
treeTable.changeSelection(treeTable.getRowCount()-1, 0, false, false);
to automatically select this new node. This works as expected, BUT I also expected it to automatically scroll to bring this new node into sight, which it does not. Instead, it scrolls to show only the penultimate node, leaving the new node hidden just slightly beyond view.
I've also tried using tricks to programmatically scroll to the final record. However, this code:
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
revealed the same problem.
Bizarrely, if I attach either of the above two blocks of code to a "Scroll to Bottom" button, and press this button manually after adding the new node, it all works fine! Please can anyone isolate the cause of this bug, or offer a fix/workaround?
JXTreeTable should have something like:
JXTreeTable.scrollPathToVisible(...)
Does this work for you?
EDIT:
I assume it works with your Button, because the Scrollpane is already "rendered/painted" with the new added node (when you click the button).based on this, the internal max/min calculation (e.g in scrollpane) can differ.
For anyone else having this problem, it seems to be caused by a peculiarity with the order in which GUI actions are executed (due in part to the need to be executed on the Dispatch thread).
From memory, this is how I solved it. (I haven't looked at the code in several months, so this code might not work perfectly, but you get the idea.)
Wrap the "vertical.setValue(vertical.getMaximum());" code in a SwingUtilities.invokeLater() method call. In other words:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
}
});

.pack() causes the components to be in the wrong place but works after window resize

I am creating a game in java using Swing.
I am using
mainJFrame.setExtendedState(mainJFrame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
on my JFrame to start it as a maximized window.
After this I am using
mainJFrame.pack();
to be able to get the correct x & y coordinates of components in the GUI. I need those coordinates since i'm painting custom images to the GUI. So far everything is fine.
When a user has passed a level, i rebuild the GUI to updated it to the new level settings and then i call pack() again.
When calling pack() the components are placed in the wrong place, slightly below where they are supposed to be.
However, manually resizing the window causes the components to go to the right place again.
I have tried calling mainJFrame.revalidate() and mainJFrame.repaint() after the pack() but this gives no effect.
If needed I can supply printscreens and code.
Thanks for your time.
Manually resizing the window validates the enclosing container and all of its subcomponents. To avoid the effect you describe, call setVisible() only after you have added components and invoked pack(). More related suggestions may be found here. If the problem persists, please edit your question to include an mcve that exhibits the problem.

Update JFrame window

I'm doing a simple java application that essentially shows a certain amount of letters (ABCDE etc) from an array, each one displayer in a portion of a grid. There are two buttons, one that will shift the letters to the left (so that one shift will become BCDEA and the right shift will go EABCD).
I've got the shifting and everything else working, as I've tested using a System output. But how do I get the window to refresh and show me the updated JLabels? They stay the same (ABCDE) after I shift them.
I've tried revalidate() and repaint() both inside the buttons' ActionListeners and on the shift method that they call, but nothing happens. Any tips on this?
I've tried revalidate() and repaint()
You only use those methods when you create a new component and add the component to a visible GUI. So it sounds like you are trying to remove/add the labels in the new order you want the labels to be displayed.
Maybe an easier approach is to leave the label in the same order but just change the text on each label. Then all you need to do is
label.setText();
and the label will repaint itself automatically without you invoking revalidate() and repaint().

Is there a way to hide the tab bar of JTabbedPane if only one tab exists?

I want a behavior similar to e.g. Firefox where the list of available tabs does only show up if at least two tabs exist.
I wasn't able to find anything like that, yet.
The best idea I had was changing the layout manually:
in case of one component, just add that to the surrounding panel
if a component is added, remove the component from the surrounding panel, add a JTabbedPane instead and add both the previous and the new component to that pane.
if a component is removed and only one component is left in the pane, remove the pane and add the contained component instead.
While this would probably work it feels like a hack or workaround...
Any better idea?
A solution should ideally work in both Java 1.5 and 1.6... but I'd be happy about a 1.6-only solution, too.
You can override the UI method that calculates the height for the tab button area, forcing the height to 0 when there's only one tab:
tabbed_pane.setUI(new BasicTabbedPaneUI() {
#Override
protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) {
if (tabbed_pane.getTabCount() > 1)
return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height);
else
return 0;
}
});
You may be better off simply using CardLayout.
I believe you'll have to do it manually. Apparently it has been done before, but only as a small bit of a system which seems to not be available.
Your approach looks good to me. I would do it just like you laid it out, and wrap all that logic in a custom JComponent so it will feel less hackish.
Yes, there is a way. Took me four hours to find at the oracle website:
http://docs.oracle.com/javase/7/docs/api/javax/swing/JTabbedPane.html#setTabLayoutPolicy()
Simply use this:
//declare
private JTabbedPane editor = new JTabbedPane ();
//construct like this:
editor.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
//just add components and see how it goes.
editor.addTab("", newPanel);
Another option would be to customize the L&F delegate (either BasicTabbedPaneUI or WindowsTabbedPaneUI depending on the platforms you care about) used by the JTabbedPane. This would allow you to customize the behavior of the tabbed pane in the case where only a single tab was being shown.
This is another way of doing things however I would say it's quite an undertaking and doing what Michael said will get you where you want to go with a lot less effort. I just wanted to post this as an answer in case you weren't aware of this option.
I think this can be achieved using tab bar and a card layout,
add the tab bar and card layout to a grid bag layout so that they
re-size automatically
the max height of the tab bar should be the height of the tab
add a listener to tab bar so that when certain tabs are clicked it
will switch the card layout to show appropriate content
hide the tab bar if it has only one tab
and this should do the job.
jTabbedPane1.removeTabAt(0); seems to work after initComponents();

How do I dynamically add Panels to other panels at runtime in Java?

I'm trying to get into java again (it's been a few years). I never really did any GUI coding in java. I've been using Netbeans to get started with this.
When using winforms in C# at work I use a usercontrols to build parts of my UI and add them to forms dynamically.
I've been trying to use JPanels like usercontrols in C#. I created a JPanel form called BlurbEditor. This has a few simple controls on it. I am trying to add it to another panel at run time on a button event.
Here is the code that I thought would work:
mainPanel.add(new BlurbEditor());
mainPanel.revalidate();
//I've also tried all possible combinations of these too
//mainPanel.repaint();
//mainPanel.validate();
This unfortunately is not working. What am I doing wrong?
I figured it out. The comments under the accepted answer here explain it:
Dynamically added JTable not displaying
Basically I just added the following before the mainPanel.add()
mainPanel.setLayout(new java.awt.BorderLayout());
Swing/AWT components generally have to have a layout before you add things to them - otherwise the UI won't know where to place the subcomponents.
BFreeman has suggested BorderLayout which is one of the easiest ones to use and allows you to 'glue' things to the top, bottom, left, right or center of the parent.
There are others such as FlowLayout which is like a textarea - it adds components left-to-right at the top of the parent and wraps onto a new row when it gets to the end.
The GridBagLayout which has always been notorious for being impossible to figure out, but does give you nearly all the control you would need. A bit like those HTML tables we used to see with bizarre combinations of rowspan, colspan, width and height attributes - which never seemed to look quite how you wanted them.
I was dealing with similar issue, I wanted to change the panel contained in a panel on runtime
After some testing, retesting and a lot of failing my pseudo-algorithm is this:
parentPanel : contains the panel we want to remove
childPanel : panel we want to switch
parentPanelLayout : the layout of parentPanel
editParentLayout() : builds parentPanel with different childPanel and NEW parentPanelLayout every time
parentPanel.remove(childPanel);
editParentLayout();
parentPanel.revalidate();
parentPanel.repaint();
As with all swing code, don't forget to call any gui update within event dispatch thread. See this for why you must do updates like this
// Do long running calculations and other stuff outside the event dispatch thread
while (! finished )
calculate();
SwingUtilities.invokeLater(new Runnable(){
public void run() {
// update gui here
}
}
mainPanel.add(new BlurbEditor());
mainPanel.validate();
mainPanel.repaint();
Try mainPanel.invalidate() and then if necessary, mainPanel.validate(). It also might be worth checking that you're doing this all in the event dispatch thread, otherwise your results will be spotty and (generally) non-deterministic.

Categories

Resources