When we are creating a component, this component takes some initial values and it will be styled accordingly to the active Look & Feel. For example, a JButton will start with horizontal alignment equals to 0 (probably SwingConstants.CENTER).
My question is, are these look and feels able to "change-override" these initial values of the default components (I am not talking for NimbusButton extends JButton). Again, I'm referring only to this kind of properties all components have, and not styles and how they look (even if some of these properties affect the appearance of the component).
I have tested the previous value (JButton.horizontalAlignment) with Java's look and feel, my system's look and feel (Windows 10) and Nimbus. The value seems to remain the same. But what about other LAFs, or other values from other Components?
One could say, that my question can be experessed also as "What look and feels are able to change"?
public class LookAndFeels {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
// No look and feel
System.out.println(new JButton().getHorizontalAlignment());
// Nimbus
setNimbusLAF();
System.out.println(new JButton().getHorizontalAlignment());
setSystemLAF();
System.out.println(new JButton().getHorizontalAlignment());
});
}
private static void setSystemLAF() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
private static void setNimbusLAF() {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
this question can't have a full answer (trust me when i say it's too complicated ,I'm implementing one from scratch and it's 4 month now and i don't even know if I'm close to call it!) .
My question is, are these look and feels able to "change-override" these initial values of the default components
yes and no,they can change such properties but it's them responsibility to honor'em .
plus most of the initial values are first to show-up look and feel values, here this will give you a hint about what happend at your first println
the real answer will be checking the ComponentUI implementation of the one you are asking about .
you are a coder so it's not a shock for you to find out what they can change is the same as any code out side of it's original package (since code is code!!).
I have tested the previous value (JButton.horizontalAlignment) with Java's look and feel, my system's look and feel (Windows 10) and Nimbus. The value seems to remain the same. But what about other L&F's, or other values from other Components?
every ui object have a method called installUI(JComponent c) this question answer is wrapped around this method , this method is called when you change the ui and depending on the implementation their ,they change some of the properties to fit them need's (E.g take any of the dark L&F's as an example ,them implementation for AbstractButton ui will have to set the background to a dark color and the foreground to bright color so the text would appear since they are dark ).
"What look and feels are able to change"
many people think that the ui control a small bits of the component , but in real it's all from the ui !! for instance AbstractButton have text , many people think that the ui is not responsible to display it but it's other way around !, if the ui didn't call Graphics.drawString(AbstractButton.getText(),x,y) (or something equivalent) somewhere in their paint(Graphics g,JComponent c) method no text will be
so the short answer to your question 'every thing on the screen' .
Related
I am working on a java swing application using synth Look and Feel.
There are already styles for every possible swing component
I must change the whole application's LookAndFeel, redefining different styles for every possible swing component.
I am now working on a sandbox, launched outside of the application. The sandbox loads my new set of styles, while the application still loads the old ones. No problems for now
However, I must then integrate it 'progressively' in the application. Meaning that in the same java application, some HMIs must use the old set of styles, while some must use the new ones
The difficulty being that each set of styles define synth "region" styles that automatically apply to the corresponding component, and I don't know how deal with several region styles that correspond to the same component type
Anybody has an idea of how I can do this ?
I saw that in swing's UIManager, one can change the LookAndFeel, but it then changes for the whole application
Only workaround I saw on the internet was to change the LookAndFeel before instanciating a Component, then change it back, which looks like an awful solution
Thanks in advance
Only workaround I saw on the internet was to change the LookAndFeel before instanciating a Component, then change it back, which looks like an awful solution
This is a very very very x 10 times bad solution.
I'm the author of the material-ui-swing and with the material style, you need to work with this concept of different style, and this is the main focus that I had during my development with the library, also because at the same time we integrate the library in one of the famous swing application called JMars where we need to respect a design system given by the UX team.
To make an example, material-ui-swing give two types of API:
one it the Material Theme System to define in a declarative way the theme around the App, and
the second is to give the lower-level API to implement the UI component with a different style.
In your case, we need the second power of material-ui-swing which is the lower-level API, and I will add an example also reported inside the repository online at the following link, and the complete doc is available here
A possible example of customization is the following on
public class ContainedButtonUI extends MaterialButtonUI {
//The propriety order inside the method installUI is important
//because some propriety should be override
#Override
public void installUI(JComponent c) {
super.mouseHoverEnabled = false;
super.installUI(c);
super.mouseHoverEnabled = true;
super.colorMouseHoverNormalButton = MaterialColors.PURPLE_500;
super.background = MaterialColors.PURPLE_700;
c.setBackground(super.background);
if(super.mouseHoverEnabled){
c.addMouseListener(
MaterialUIMovement.getMovement(c, this.colorMouseHoverNormalButton)
);
}
//If you want use this style also for Default button
// super.defaultBackground = MaterialColors.PURPLE_700;
//super.colorMouseHoverDefaultButton = MaterialColors.PURPLE_500;
super.borderEnabled = false;
}
After that to keep all your app architecture clean you can add the following specialization of JButton
/** #author https://github.com/vincenzopalazzo */
public class ContainedButton extends JButton {
public ContainedButton() {}
public ContainedButton(Icon icon) {
super(icon);
}
public ContainedButton(String text) {
super(text);
}
public ContainedButton(Action a) {
super(a);
}
public ContainedButton(String text, Icon icon) {
super(text, icon);
}
#Override
protected void init(String text, Icon icon) {
super.init(text, icon);
// When you don't want anymore you just delete the
// following line
setUI(new ContainedButtonUI());
}
Of curse, maybe the library can not help you in all your component styles, but nobody said that the library can not evolve with the help of the community.
A not complete description of components can be found here
Thank you all for your answers and sorry for my lack of reactivity during the holidays
I think I found a solution, which is not exactly what I asked for but answers my problem :
I will not make several LookAndFeels coexist.
Instead, I will load all styles, new and old, in the same LookAndFeel, and use different setName() for new and old components
For region styles (which was the problematic here), I will make a custom SynthStyleFactory which will redirect to the correct region style
Once all HMIs are migrated, I will delete the old styles and the custom factory which won't be needed anymore
I'd like to implement a ToolTip in Swing that has customised behaviour: the longer the user hovers over the component, the more detail should be shown in the tooltip (i.e., a few new lines are added after a few seconds of the user hovering over the component). I just need to check whether this is really doable with Swing without things getting too messy. My idea at the moment would probably be:
Extend ToolTipManager
Override mouseEntered to start a timer (maybe use javax.swing.Timer?). Call setToolTipText and createToolTip to refresh the tooltip and add new information at regular intervals
Override mouseExited to reset the timer
Probably use setDismissDelay to set the dismiss delay to something a lot longer (or Integer.MAX_VALUE)
Is such a thing feasible or is this not a good way to work with Swing (sorry, I'm pretty new to it)? Is there a better way of doing this?
[edit] Hmm, just remembered that ToolTipManager is a singleton with a constructor that only has package visibility, so it can't be extended.
[edit 2] I'm trying out a few solutions at the moment. One thing that I forgot to add is that I do need to know which component is being hovered over - which I guess means I'll need to be working with some sort of listener with a mouseEntered() method (or be able to access this information). And no other interactivity with the popup/tooltip is needed - it just needs to display information.
(This may seem a bit confusing so let me know if you need me to clarify let me know and I'll try to show you how I picture the code) I think your idea might work like if you extend it, and also make a private class that extends Threadand then in the run() method you do something like
while(true)
{
sleep(1);
timeElapsed++;
}
And in your class that extends ToolTipManager, create a field for that class that extends Thread and in the mouseEntered(MouseEvent e) instantiate the thing like:
extendsThreadClass = new ExtendsThreadClass();
extendsThreadClass.start();
and then in the mouseExited(MouseEvent e) method do
extendsThreadClass = null;
Then in that mouseEntered(MouseEvent e) method after starting the Thread then you can do what you want to do after the time thing like
if(timeElapsed > 3000)
{
//what you want to do here
}
Sorry it may be confusing, let me know if i can clear it up for you
I thought I should update this with the approach I took before I saw l1zZY's answer (which I think is the better way of doing things - I still had bugs in my code before I moved onto something else, but this might still be helpful to someone). This is what I did:
Extend JToolTip
Use a Swing Timer for timing
Add a MouseMotion listener to the JTree (in my case I wanted the popup to show when a node was hovered over)
Somewhat inelegantly, detect when the mouse moved over a tree node like this:
public void mouseMoved(MouseEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
TreePath path = getPathForLocation(x, y);
if (path == null) {
tooltip.hide();
} else {
TreeNode node = (TreeNode) path.getLastPathComponent();
tooltip.setHoveredNode(node);
if (!tooltip.isVisible) {
int absX = e.getXOnScreen();
int absY = e.getYOnScreen();
final Popup tooltipContainer = PopupFactory.getSharedInstance().getPopup(PDTreeView.this,
tooltip, absX, absY);
tooltip.setToolTipContainer(tooltipContainer);
tooltip.show();
}
}
}
tooltip.show() refers to how the tooltip was contained in a Popup
in order to show or hide it programmatically. show() shows the
Popup (and therefore tooltip) and also starts the Swing timer.
Timer has a method called actionPerformed() which is called at whatever interval you set. I just had that method call the code that adds new information to the tooltip. in hide(), I reset the tooltip and the timer.
I had issues with the popup or tooltip not resizing to fit the content, but otherwise this seemed ok.
To align my JFrame from righ-to-left, I use:
setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
but this works only if I use the following style (decoration) of the JFrame:
public class RightToLeft {
public static void main(String []args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run() {
try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); }
catch (Exception e) { e.printStackTrace(); }
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("العنوان بالعربي");
frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
frame.setSize(300,300);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
but I want it to work without this decoration. How to solve this issue?
EDIT:
#mre I want a JFrame like this one:
EDIT2:
I really really need this issue to be fixed, so I offer 500+ to who will give a JFrame like this (with WindowsLookAndFeel):
The following explains what you observe through your code snippet:
ComponentOrientation is applicable only to Swing (and AWT actually) components.
When using JFrame.setDefaultLookAndFeelDecorated(true);, then the whole frame decoration is performed by Swing (the LookAndFeel itself in fact), so this works as expected.
If you don't set this option though, that means that the OS is in charge of the frame decoration, however the OS cannot be aware of the ComponentOrientation used by your Swing components!
I expect the OS (did you mention what OS you use exactly? It seems to be Windows 7 right?) to perform the correct decoration based on the currently selected Locale. Hence if you have an Arabic locale setup and selected on your OS, I guess all windows decorations are right to left. Did you try changing that Locale (through the "Region and Language" control panel)? Did it work?
Note: I don't think that you can change these OS settings directly from Java, but you can read them with Locale.getDefault().
To sum up:
first of all, you have to ensure that your OS is properly configured in terms of text orientation; sorry I can't help much here because I don't have any right-to-left languages installed on my Windows machine.
then, use the system look and feel and ensure that JFrame.setDefaultLookAndFeelDecorated(false);
if that doesn't work, then you may consider posting your code snippet, along with your system configuration to Oracle Java bugs list
What follows are extra notes on how to improve this part of your code (but this is not a fix for your issue)
By the way, if you let the user define its OS language preferences, then you shouldn't explicitly hard-code frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); but rather use something like:
frame.applyComponentOrientation(
ComponentOrientation.getOrientation(Locale.getDefault()));
Where Locale.getDefault(), unless explicitly changed within your application, will return the OS-level currently selected Locale.
Also note that it is preferable to use applyComponentOrientation() rather than setComponentOrientation(), because the former recursively sets the given orientation to the whole hierarchy of components.
Finally, you will have to ensure in your windows that the LayoutManager used is right-to-left aware; this is normally OK with all standard Swing layouts, but not for all 3rd-party layout managers, if you use some.
#Eng.Fouad
just joke and this one ???...
code:
import java.awt.ComponentOrientation;
import javax.swing.JFrame;
import javax.swing.UIManager;
import org.pushingpixels.substance.api.skin.SubstanceOfficeSilver2007LookAndFeel;
public class RightToLeft {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
//UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
UIManager.setLookAndFeel(new SubstanceOfficeSilver2007LookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("العنوان بالعربي");
frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
});
}
private RightToLeft() {
}
}
I suspect it has to do more with the OS. Normally (if you don't call setDefaultLookAndFeelDecorated) it is the OS that provides the frame decoration, not the LAF.
You should try changing your preferences in the OS to say you want right to left orientation.
Sorry, I don't know where those settings would be.
Once you do this, then you should be able to remove the call to setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
as the LAF will pick up the OS settings from the Locale.
EDIT
This Link describes how to enable right-to-left text on Windows 7. Then I think you would also need to change your locale.
It looks like the Component Orientation feature is not supported with the Windows LookAndFeel (at least not for the title bar)
Here is one posibility. This utility is designed for Mac users who have switched to Windows and want the window buttons on the left, but it should serve the same needs as yours.
This solution has nothing to do with Java (so I don't know if it's even acceptable for your needs) and sounds like it would be external to your application. I have not been able to try it out myself (I'm not running Windows), so I can't vouch for it, but it might be worth a try.
You only need to add this line of code and I am sure it works 100%.
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
Is there an example out there for using IDecorationContext for label decorations?
By the looks of it, IDecorationContext class seems to provide some sort of contextual decoration support, but for the life of me, I can not find any sample code using this feature...
Has anybody actually used decoration context feature and if so, what use cases did it solve?
PS: I am looking for a way to apply image decorations to object labels and depending on where the object is displayed, the base icon size varies (e.g. traditional "small" icons in table- and tree items and larger icons for content headers).
The decorations applied to the original icons should choose appropriate size decorations accordingly.
IDecorationContext seems to fit the bill for what I need it for, but the documentation is as sparse as one can expect from a minor feature of an open source library and there are no examples to be found.
Googling for the "IDecorationContext" did not reveal anything interesting either, so I turn to StackOverflow crowd wisdom in hopes next guy getting the question would be able to get their answer faster ;)
I did not use IDecorationContext, but you can see it used in org.eclipse.jface.viewers.LabelDecorator.
It is also discussed in this thread (even if there are no answer, that can at least give you a starting point)
My current approach is to extend org.eclipse.ui.decorators using a
ILightweightLabelDecorator to add a replace overlay to the respective
icons:
public class ProjectLabelDecorator extends LabelProvider
implements ILightweightLabelDecorator {
...
public void decorate(Object element, IDecoration decoration) {
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
try {
if (folder.getProject().hasNature("rttdt.nature")) {
if (ProjectNature.isTestcase(folder)) {
IDecorationContext context =
decoration.getDecorationContext();
if (context instanceof DecorationContext) {
((DecorationContext) context).putProperty(
IDecoration.ENABLE_REPLACE, Boolean.TRUE);
}
decoration.addOverlay(fTestcaseOverlay,
IDecoration.REPLACE);
}
} catch (CoreException e) {
}
}
}
...
}
Ok so ive got a swing app going using the "System" look and feel. Now, I want to change the background colour of the main panels to black. Too easy right?
UIManager.put("Panel.background", Color.BLACK);
Well yeah, except now the controls in the app look stupid, because their 'shadows', for want of a better word, are graduated to fade towards the old system default colour(gross windows grey). So there are light grey 'corners' on all the controls, especially the tabs on JTabbedPane.
I know it can be fixed, because if you change the windowsXP theme to one with a different default application colour, the controls take on this changed colour and their shadows 'fade' towards it.
But I have no idea what UIManager key it is, or even if you can do it with UIManger.
I dont really want to change the L&F engine, because apart from this it looks good.
You might try these:
control
controlDkShadow
controlHighlight
controlLtHighlight
controlShadow
(I just found them in this list: Swing [Archive] - UIManager: setting background and JScrollBar )
In general this is a little bit tricky. It depends on exact LaF you are using.
For example. JGoodies use own color scheme which redefine this stuff.
In general the property names are composed like
COMPONENT_NAME_WITHOUT_J + '.' + PROPERTY.
Unfortunately, property names can be only obtained from implementation classes of LaF. These aren't shared or something. Each component has its own. Or better, it depends on laziness of author which pairs he used. In general.
A lot of help makes redefine Panel.* and Button.. A lot of components use Button. properties.
Try, play, win :). I wish you luck :).
PS: It is a lot of properties to overwrite. But this is the way how LaFs works.
You can see what the default settings (and their keys) are by using UIManager.getDefaults();
You can then iterate over the resulting keySet (it is an instance of Map).
So something like this will show all the default keys:
for (Object key: UIManager.getDefaults().keySet())
{
System.out.println(key);
}
Some controls like JButton need to have setOpaque(false) called to allow the new background colors to fade through.
To list out all the possible options that we can set to UIManager to change LaF, run the code below ........
import java.util.*;
import javax.swing.UIManager;
public class UIManager_All_Put_Options
{
public static void main (String[] args)
{
Hashtable properties = UIManager.getDefaults();
Enumeration keys = properties.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
Object value = properties.get (key);
System.out.printf("%-40s \t %-200s \n", key,value);
}
}
}
enjoy...