Under certain circumstances, I need a JTabbedPane to remain on one pane until the user supplies certain information. Essentially, when this circumstance occurs, I need the current pane to become modal.
How can I implement this? I was thinking I could catch whatever event is triggered when the pane changes, and reset back to the pane I want to stick on. But I'm worried that this won't be quite right, that depending on when the event actually fires the transition to the new pane will happen after I call the method to set the pane to the pane I want, or some other similar race condition. Is there a better way? Is there a way I can make this approach work?
I would suggest setting the other tabs to disabled. This has a positive effect of providing the user feedback that they cannot click out of the tab. Otherwise they may be madly clicking and wondering why it will not let them leave the tab.
Simply set them enabled again after the required fields are completed.
just disable the JTabbedPane:
pane.setEnabled(false);
and enable it if all fields are correctly set (or whatever condition)
You could use a CardLayout along with JPanels to do what you want and not use JTabbedPanes. Since you need to use the tabbed panes, I would suggest that once the condition has been reached that you want to force the user to stay on that tab set that tab to be the selected one by using.
setTabComponentAt(int index, Component component)
or
setSelectedIndex(int index)
Set a flag indicating that the user should not be able to proceed until completing whatever it is you want them to do and have all the other tabs be disabled using setEnabledAt(int index, boolean enabled)
.
Once the user has completed what they needed in order to continue set the flag accordingly and reenable the other tabs.
I haven't the time to try that solution out but I think it should work.
Related
I am trying to have a JavaFX Pane (VBox in my case, but I don't think it matters) that has a ContextMenu that behaves correctly.
I have found these two questions: why Panes can't have ContextMenus and How to create ContextMenu within a Pane.
The problem I'm having with these two solutions (which are very similar) is that while the context menu correctly disappears if I click on the pane, it doesn't disappear if I click inside a control within that pane. The simplest way to observe this flaw is to create such a pane with a TextField. Right-click on the pane to show the context menu, then click inside to TextField to focus on it. While a proper context menu would disappear at this point, this "hacked-in" context menu (for lack of a better term) happily stays in it's place, possibly blocking the user's view of the text field they are trying to fill.
Now, I know I can add a change listener to the focused property of each and every control on my pane, but that feels redundant. Is there a better way to make sure the context menu is hidden when a control in my pane is selected (or, more accurately - when the user click the mouse anywhere in the owning window outside the context menu)?
What I tried so far and doesn't work -
Adding a change listener to the pane's focused property - it appears the pane isn't considered focused if one of it's children is
Adding a change listener to the context menu's focused property - it appears the context menu's focus isn't changed when clicking outside of it.
Ok, so after some digging in the source code for JavaFX I have found this workaround (which is what "solves" this problem for normal controls). Simply add this line of code -
contextMenu.setImpl_showRelativeToWindow(true);
Now, I know using internal implementation methods is discouraged as they may disappear, but this is the only solution I have found. If anyone has a better solution I'll be glad to hear it, but I suspect this is simply a bug that should be filed (i.e. - there should be a way to use the showRelativeToWindow mechanism when setting context menus on panes).
I guess a somewhat safer solution would be to have a throwaway control (not in the scene graph) on which to set the context menu, but I don't know if this would have any unwanted side effects:
Label throwaway = new Label(); // No special reason for using Label, could be any Control.
throwaway.setContextMenu(contextMenu); // note that this is the only place `throwaway` is used, it is never added to the scene graph
// or referenced again, but just setting the context menu on a control solves the problem.
Edit
After digging some more I have found this, and a solution in the discussion - you should call the show method overload which takes a window, not a node! Not very clear, but it works:
myPane.setOnContextMenuRequested(event ->
contextMenu.show(myPane.getScene().getWindow(), event.getScreenX(), event.getScreenY())
);
I use a c1.setFocusable(false) so that my Checkbox c1 is not focused as shown in the pic and after that, focus goes to the next Checkbox c2, I use again c2.setFocusable(false) and it continues to all Checkboxes. Even if it doesn't find one, it continues to the next JTextField. Is there a way to not let the focus go to something instead of setting focusable to all my numerous components?
how can I get rid of that grey border thing?
Why would you want to do that? That "grey border thing" is the focus indicator. It is used to provide information to the user so the user knows which component has focus.
If you really want to confuse the user and make them guess which component has focus then you can use:
button.setFocusPainted( false );
I'm working on a program that traverses a list of objects every pass through. When an object's "time" (i.e. number of passes) is up, it brings up a JDialog to request its next task. The problem is how to deal with multiple objects making this request on a single pass.
If I make the JDialog modal, each object makes its request in turn, which is what I want; but the main program window is unavailable, which I do not want.
If the JDialog is not modal, multiple instances appear, one for each pass, which I do not want; or, if I set it up as a singleton, only one instance appears but only the last object int the list to make the request gets to use it.
Is there a way I can pop up the dialog, leave other windows available to the user, and have the other objects wait their turn to use the dialog?
As noted in comments, the problem can be addressed by altering the modality of the parent Window. On the downside, the "behavior is implementation-dependent."
As an alternative, consider traversing the objects in the background thread of a SwingWorker and adding new candidates to a suitable component, e.g. JList, JTable, or JTabbedPane. Selecting an element form the component would bring up a conventional modal dialog, removing the element on completion. A related example is shown here. Each of the suggested components can be labeled with an icon representing its status.
Solved this by setting the original window's ModalExclusionType to "application" and toggling back to "no exclude" once finished.
I have a JPanel with a set of items (for example combo boxes and text fields). Some action listeners are implemented on those items to register user updates.
If the user selects a value in a JComboBox (for example), the action listener captures the event. The corresponding underlying bean method is called and the panel is refreshed. Changing can have an impact on other fields displayed in the pane.
The problem is that when the panel is refreshed, all listeners are triggered, and they call for a refresh themselves. This leads to an infinite loop.
How can I avoid this? I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the panel content.
One option is to have a central boolean value or some indicator that each listener can check to prevent the chaining of events.
Another option is to not refresh the field if the value does not change. That way each component is updated at most once per refresh.
I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the pane content
Then remove the listeners, refresh the pane content and then restore the listeners. This way the listeners only fire when a user change is made.
I think that if your problem is in combobox it just points to a bug. Really, if user changes the value of the combobox, that somehow triggers refresh of the pane the value of the combo box should not be changed second time! So if it is onValueChanged() (or something like this) it should not be called at all when pane is being refreshed.
But if for some reason it happens you can verify whether the old and new values are the same and exit the listener.
If this still does not help I'd suggest you some non-standard solution: try to investigate the stack trace into the listener. Can you identify whether the listener was called as a direct reaction to user's action or after the pane refresh? In this case you can create utility method and put it in the beginning of all relevant listeners.
My applications also suffered from this problem, and solution with the flag, that I should check in every listener and enable/disable in code, feels not very good for me. I always forgot to set this flag to true/false in necessary places.
That is why I decide to implement another solution.
I just subclass all default swing components that I am using often, and implemented custom ValueChanged event that I fire after mouse/keyboard/clipboard/etc events. Now I am always know, that if ValueChanged event is fired, it means, that value was issued by user, not by code. Event handling in this way much more cleaner. This solution solves my problem.
I have a menu with a few JCheckBoxMnuItems. How do I ensure that the Menu stays open until I have done all my selections (i.e. checked the menuitems) and does not close on just clicking one of them?
I'd rather not try to change the normal menu behavior for an application or for a part of the menu tree. A User expects that the menu closes automatically after a menu item is clicked. And, if you kept the menu expanded, what kind of action would you invent to close it manually after you've done your last selection?
If there's a requirement to change more then one setting within one use case, then you should consider to provide a small dialog where the use can apply the changes and confirm them at once. I believe, that's more consistent with typical behaviors of UIs.
And it declutters the menu bar, you'll have just one 'setup' menu item instead of a dozen (?) check box actions :)
I guess menu's aren't supposed to allow multi-selection.
But you may offer keyboard shortcuts to set the menuitems without using the menu at all.
If the set-operation of your flags is a central aspect in your application, I would tend to use a dialog here. These are all suggestions which do not require to change the internal implementation of the existing controls, even though I know, that it would be possible in swing.
I agree that it is better to do this with standard UI. However, if do you want to add checkboxes that do not close the menu it is surprisingly easy:
JCheckBox checkBox = new JCheckBox("Text");
checkBox.setOpaque(false);
checkBox.setRequestFocusEnabled(false);
menu.add(checkBox);
This may not work on every look and feel and the check boxes will not line up with menu items in the same manner as JMenuItems but it seems to be a reasonable place to start.