In my program I would like the user to be able to change the colour scheme of the program. I have a method which passes in a colour to set the background of all the panels using UIManager.
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(this);
}
However the issue I'm running into is that it is not changing the colour of the panels. This method is located in the class for the JFrame.
Copied directly from Swing API Docs.
public static void updateComponentTreeUI(Component c)
A simple minded look and feel change: ask each node in the tree
to updateUI() -- that is, to initialize its UI property with the
current look and feel.
Notice the emphasize. Your code doesn't work because you are passing this as the argument to updateComponentTreeUI(). Since you are passing your panel as the argument, only the components inside the panel and the panel itself will get their UI updated. You have to pass the container that holds all other panels in your program, that is your JFrame
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(frame); //instace of your frame
}
simply because the UIDefaults changes is only effective for the newly created components
for your feature to work you have to make the app restart then before starting the gui change the background in the defaults in the UIManager , or you will have to do it manually (panel by panel).
if you do it manually recursion will help a lot ,like this
private static void loopForPanel(Container c, Color col) {
synchronized (c.getTreeLock()) {
for (Component com : c.getComponents()) {
if (com instanceof JPanel) {
com.setBackground(col);
}
if (com instanceof Container) {
loopForPanel((Container) com,col);
}
}
}
}
then in your changeColourScheme method pass the frame it self for the loopForPanel method with your desired color.
Related
I have a JFrame containing the usual assortment of panels and widgets, and I have a JPanel which I'm using as that JFrame's glassPane. I'd like to be able to restrict keyboard focus traversal to the components in the glassPane when it's visible.
My problem may or may not be compounded by the fact that a background thread launches a process which results in a progress dialog appearing and subsequently disappearing, which steals the focus from a widget in my glassPane but returns it to some widget beneath my glassPane.
I've tried setting the JFrame's focus traversal policy to one which only allows the glassPane to be focused, but that didn't seem to have any effect. (maybe I did it incorrectly?)
Any help would be appreciated.
As mentioned in my comment, I would probably go for a J/X/Layer - but here's a solution if indeed a custom FTP is the only missing piece in your context.
To exclude components from focus traversal, reject them in the accept method of a custom FTP. Below is an example which rejects all components which are not children of the glassPane if the glassPane is visible (note: this is overly simplistic, as it handles direct children only, real-world code must walk up the parent chain until it either hit the glasspane or not)
public static class FTP extends LayoutFocusTraversalPolicy {
#Override
protected boolean accept(Component comp) {
JFrame window = (JFrame) SwingUtilities.windowForComponent(comp);
if (hasVisibleGlassPane(window)) {
return comp.getParent() == window.getGlassPane();
}
return super.accept(comp);
}
private boolean hasVisibleGlassPane(JFrame window) {
return window != null && window.getGlassPane() != null
&& window.getGlassPane().isVisible();
}
}
I'm going to make my comment an answer as I believe that it is the solution to your problem:
Perhaps a better option is to use a CardLayout to simply swap views rather than a glass pane or dialog since this situation does not seem the best suited for glass pane use. If you use CardLayout, you don't have to come up with a kludge (fiddle with focus traversal), to fix the side effects of another kludge (using glass pane for things it wasn't intended to be used for).
If you're not familiar with it, a CardLayout will allow you to easily swap components in a GUI, and is often used to swap JPanels that hold complex GUI's that occurs when the user goes from one major program state to another. I think that it would be perfect for your purposes and would prevent you from having to worry about your focus issue.
Try calling .setFocusable(false) on the JFrame you want to ignore (or at worst, on all the components within it) while the glassPane is visible.
When I had discussion with client he gave me the same requirement. So I decided to use the JLayer and LayeredPane in my project and with all of these components as a simple solution I implemented following code, may be it will help you in your project.
public YourConstructor() {
yes.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent fe) {
if (!no.hasFocus()) {
no.requestFocusInWindow();
}
}
});
no.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent fe) {
if (!yes.hasFocus()) {
yes.requestFocusInWindow();
}
}
});
}
#Override
public void setVisible(boolean visibility) {
super.setVisible(visibility);
if (visibility) {
yes.requestFocusInWindow();
}
}
To build upon kleopatra's answer and to help others in a similar situation; I had a bunch of custom swing components that I didn't want focused (sometimes only when they weren't editable). I ended up with this:
/**
* A custom focus traversal policy to make focus traversal inside a container to ignore some swing components.<br /><br />
*
* <b>Ignored components:</b><br />
* - <code>CustomComponent1</code> components<br />
* - <code>CustomComponent2</code> components that are not editable<br /><br />
*
* <b>Usage:</b><br /><br />
* <code>Container.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());</code>
*/
public class CustomFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
private static final long serialVersionUID = 1L;
protected boolean accept(Component c) {
if(c instanceof CustomComponent1) {
return false;
}
if(c instanceof CustomComponent2) {
CustomComponent2 t = (CustomComponent2) c;
if(!t.isEditable()) {
return false;
}
}
return super.accept(c);
}
}
Note that the policy needs to be set for each Container (I did it for each Window I was creating):
Window window = new JFrame(); // Or JDialog; both subclasses of Container and Window
window.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());
Im using a JPanel with propertyChangeListener and want it to rerender itself based on whenever a particular variable model changes. My code for the same is as follows --
public class LabelMacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private LabelMacroModel model;
public LabelMacroEditor(LabelMacroModel bean) {
this.model = bean;
model.addPropertyChangeListener(this);
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
JScrollPane macroModelScroller = new JScrollPane(allButtons);
macroModelScroller.setPreferredSize(new Dimension(300, 200));
for(MacroModel macroModel : model.getMacroModelList())
{
LabelMacroEditorEditableEntity macroEditorEntity = new LabelMacroEditorEditableEntity(macroModel);
Box entityBox = Box.createHorizontalBox();
entityBox.add(macroEditorEntity.getUpButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getMacroDetailsButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getDownButton());
allButtons.add(entityBox);
}
add(macroModelScroller);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
When i use the debug mode in eclipse i can see that whenever there is a change to model it triggers off the call propertyChange and it also runs over revalidate and repaint but only the JPanel display remains the same. It does not seem to be rerendering itself.
Anything fundamental that I'm missing here ?
EDIT :
An example snippet of a property im changing is as follows --
labelMacroModel.addMacroModel(addedMacroModel);
where labelMacroModel is of the type LabelMacroModel and addedMacroModel is of the type Macro
Now the relevant part of LabelMacroModel class that fires off the property change is as follows --
private List<MacroModel> macroModelList;// this is the list of all MacroModels
public void addMacroModel(MacroModel macroModel) {
macroModelList.add(macroModel);
pcs.fireIndexedPropertyChange("LabelMacroModel", macroModelList.size(), null, macroModel);
}
Its not clear how you are changing the components in the panel. If panel is not updated then repaint/revalidate will have no effect. I think you should not need revalidate/repaint to be called explicitly if you are not modifying the way components are laid out. JButton.setText should for example change the label of the button without need of calling repaint.
To expand on the answer by AKJ above, I think you should be reconstructing your components on property change. So doing a remove all then readding is one way to do this. Once you get this working you could be more selective about pushing the model update into the GUI eg if a new entry has been added then just add a new component to reflect this. The remove all / readd is fine for a lot of cases though. HTH.
In my swing-based UI, I have a JMenuBar which contains a a series of JMenu and JMenuItem objects. One of the menu-item objects also happens to be a JCheckBoxMenuItem.
Now, while the user can click on this JCheckBoxMenuItem in order to toggle the state of an application level setting, the user (in my application) also has access to a command line API to change the application setting. The details of this command line API are not relevant.
My question is this: When the user goes through the command line API and toggles the state of the setting (a static property / setting that applies to all open instances of my application), I would like to update the "checked / unchecked" property on the JCheckBoxMenuItem. To do this, I can either:
Store a reference to the checkboxmenuitem.
Traverse the JMenu container hierarchy to find the checkboxmenuitem.
I don't want to use method 1 because in the future, if I have more of these checkboxmenuitems, then i'll have to hang on to a reference to each one.
Method 2 seems cumbersome because I need to do:
Component[] childComponents = menu.getComponents();
for(Component c:childComponents)
{
if(c.getName().equals("nameOfTheCheckBoxMenuItem"))
{
componentFound = c;
}
}
Is there a better / more efficient way to find a component in a component hierarchy? Is there maybe a better way to solve this problem in general (changing the state of the jcheckboxmenuitem when the value of a property in my application changes), using say, a PropertyChangeListener (Although my understanding is that these only work on "beans").
1) I'd suggest to use CardLayout for nicest and easiest workaround for multi_JPanel application
2) then you can imlements
add Action / ActionListener
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (myCheckBox.isSelected()) {
// something
} else {
// something
}
}
};
add ItemListener
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (Whatever) {
// something
}
}
};
I am currently working on a Swing Applet, and am having issues referencing my custom AWT Canvas component (very simple extended class) in other class functions, such as with any other component (i.e. button) normally created with Netbean (7.0)'s designer.
My custom canvas element I add here, I was sure this would be the appropriate place (especially after all other generated components were just created in the same area)
public void init() {
try {
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
CustomCanvas myCan = new CustomCanvas();
myCan.setBounds(100, 100, 200, 200);
getContentPane().add(myCan);
...
However, unlike the generated components, I cannot access them by name and cannot seem to access them through other means (this.myCan) either. I have set up a sample function that will handle a (generated) button on the Swing form to manipulate the previously instantiated myCan component:
private void btnManipCanvasActionPerformed(java.awt.event.ActionEvent evt) {
//Essentially Was wanting to call something here such as myCan.getGraphics().setRect...
}
Do you know of a way to access myCan there? Am I supposed to place custom initializations of components in a different area so they can be publicly accessed?
Just make the canvas an instance variable, as (I guess) all the other components created by the Netbeans designer:
private CustomCanvas myCan;
public void init() {
try {
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
myCan = new CustomCanvas();
myCan.setBounds(100, 100, 200, 200);
getContentPane().add(myCan);
// ...
there are possible issues or painting lacks because you probably mixing ATW Components with Swing JComponets,
if there nothing special that came from OpenGL, then look for JPanel instead of AWT Canvas and for all panting in Swing redirect everythigns to JLabel
please read how LayoutManagers works to avoids setBounds(int, int, int, int);
I'm looking for a place to hook some code to programmatically create, size and position a JPanel after the application has finished loading.
I'm just starting with Java. I'm using NetBeans 6.5.1 with jdk1.6.0_13. I've used the new project wizard to create a basic Java/Swing desktop application. This is a SingleFrameApplication that uses a FrameView with a central main JPanel where all the UI elements are placed.
I first tried my code in the FrameView constructor but when I try to arrange my JPanel based on the bounding rectangle of one of the design time controls I added to the UI, that control has not yet finished being positioned and sized so I'm getting all zeros for the coordinates.
I've verified my code works as expected by calling it from a click event after the application has loaded so my problem is finding a way to know when everything is finished being sized and arranged.
I also tried the componentShown event from the main JPanel but I later read that is only fired if setVisible is explicitly called which apparently doesn't happen during normal application startup.
Can anyone provide some pointers? Thanks.
Update:
In addition to what I mention in my answer below, I also read about the Application.ready() method. This would also be a point in time of interest for knowing when the UI part of an application is finished doing everything it needs to do. Communicating to my view from the application seemed a bit messy though.
The solution I went with was actually a combination of the answers from Charles Marin and JRL (I upvoted both of your answers for credit, thanks).
I had my FrameView class implement WindowListener.
...
public class MyView extends FrameView implements WindowListener
...
and in my FrameView constructor I added a listener to the application's main frame.
...
getFrame().addWindowListener((WindowListener) this);
...
Then in my implementation of windowActivated I could call the code I had to arrange and size a control on the main JPanel based on the location and size of other controls.
public void windowActivated(WindowEvent e)
{
// The application should now be finished doing its startup stuff.
// Position and size a control based on other UI controls here
}
I think you want WindowActivated. Have a look at this part of the tutorial.
I'd try using getFrame().isValid()
I assume this is the WYSIWYG editor thing. I'm looking at NetBeans 6.1, so your experiences may vary.
The traditional way to layout Swing components is by using a LayoutManager (or LayoutManager2). According to the NetBeans help, the visual editor supports these so long as they don't require support for constraints.
The procedure goes something like this:
Create a new JavaBean and have it implement LayoutManager (a BeanInfo is required too for palette support - you can create one by right-clicking the bean class)
Build the project
Right-click the bean and choose Tools > Add to Palette... and add it
Right-click the panel for which you want to set the layout and select Set Layout > Your Bean Name
You may find the design-time experience somewhat lacking.
A sample layout implementation:
public class StepLayoutBean extends Object implements Serializable, LayoutManager {
public void addLayoutComponent(String name, Component component) {
}
public void layoutContainer(Container container) {
Dimension space = container.getSize();
int xoffset = 0;
int yoffset = 0;
for (Component kid : container.getComponents()) {
Dimension prefSize = kid.getPreferredSize();
if (prefSize.width + xoffset > space.width) {
xoffset = 0;
}
Rectangle bounds = new Rectangle(xoffset, yoffset, prefSize.width, prefSize.height);
kid.setBounds(bounds);
xoffset += prefSize.width;
yoffset += prefSize.height;
}
}
public Dimension minimumLayoutSize(Container container) {
Dimension size = new Dimension();
for (Component kid : container.getComponents()) {
Dimension minSize = kid.getMinimumSize();
size.width = minSize.width > size.width ? minSize.width : size.width;
size.height += minSize.height;
}
return size;
}
public Dimension preferredLayoutSize(Container container) {
Dimension size = new Dimension();
for (Component kid : container.getComponents()) {
Dimension prefSize = kid.getPreferredSize();
size.width += prefSize.width;
size.height += prefSize.height;
}
return size;
}
public void removeLayoutComponent(Component component) {
}
}
If a custom layout doesn't fit the bill, have a look at the event bindings under the component's properties panel - though resizing that way might be a recipe for some kind of recursive event storm.