Swingx component provider: hide component on certain rows - java

I've subclassed Swingx's ComponentProvider to provide JButtons but on some lines of my JXTreeTable, I don't want to show any button. The final "result" I want is to have an empty cell, like what I get when showing an empty text in a column for which I haven't set a provider.
Is it possible to hide the rendered component on certain rows (depending on the value for example)? Setting setVisible(false) on the rendered component in format() or configureState() doesn't work.
Is it possible to subclass ComponentProvider to provide different several types of Components? If yes, how would that work?
Where can I find some examples of the possibilities that the ComponentProvider offers and a clear explanation of which method does what (for example, I hardly understand the difference between configureState() and format())?
EDIT
Is it possible to prevent a JButton displayed in a JX(Tree)?Table to be as wide as the cell?
If I create another highlighter, can I use another predicate (ROLLOVER or something) to change the cursor? The cursor change to a hand (over a link) even if the button is hidden.
Many thanks!

Interesting :-)
generally doesn't work because the CellRendererPane doesn't respect the component's visible property - it stamps it always. BUT: can work in SwingX if the actual provider it wrapped into a WrappingProvider and then that wrapper's component is set invisible.
a snippet, just as proof-of-concept
table.getColumn(1).setCellRenderer(new DefaultTableRenderer(
new WrappingProvider(IconValues.NONE, new ButtonProvider(), false) {
#Override
protected void configureState(CellContext context) {
super.configureState(context);
rendererComponent.getComponent().setVisible(context.getRow() != 5);
}
}
));
On the other hand, the provider is not the place for plugging in custom context-dependent configuration. That should be done in a Highlighter, as f.i. in
AbstractHighlighter highlighter = new AbstractHighlighter(HighlightPredicate.EVEN) {
#Override
protected Component doHighlight(Component component,
ComponentAdapter adapter) {
((WrappingIconPanel) component).getComponent().setVisible(false);
return component;
}
#Override
protected boolean canHighlight(Component component,
ComponentAdapter adapter) {
return component instanceof WrappingIconPanel;
}
};
table.addHighlighter(highlighter);
Which doesn't work as expected (the button is always hidden) because is not one of the properties which are guaranteed to be reset in the provider. Nothing prevents custom providers to extend those guarantees, like
table.getColumn(1).setCellRenderer(new DefaultTableRenderer(
// custom wrappingProvider which guarantees the reset of visible
// property of the wrapped component
new WrappingProvider(IconValues.NONE, new ButtonProvider(), false) {
#Override
protected void configureState(CellContext context) {
super.configureState(context);
rendererComponent.getComponent().setVisible(true);
}
}
));
Now the highlighter can fearlessly change the visible based on context. A slight visual glitch: the WrappingIconPanel always leaves some space for the icon, even if there is none - not quite sure why that happens or whether it would be safe (in SwingX) to remove that spacing (the wrappingProvider originally is meant for use in JXTree, it's not installed by default because there are still issues with ComponentOrientation).
(2 in the question) not supported, the componentProvider is designed to return the same single component configured with the exact same properties on each call
(3 in the question) cough ... no, nothing but the sources and examples (in the demos and the test packages)
Edit (to answer the edited part of the question)
no, with the current WrappingIconpPanel: it does use a Borderlayout which - as we all know :-) doesn't respect max size. Using a BoxLayout would but had issues that I don't fully remember. Nevertheless, that would be the place to tweak, so that the button's max would be respected
hmmm ... not entirely sure how you implemented the cursor change. Assuming that it's in your ButtonProvider: implement the isRolloverEnabled to return true/false depending on whether it is visible or not Edit-in-Edit doesn't work. Don't know why, could be that this is a bug in rollover-dectection and/or handling in WrappingProvider
Off into the weekend now :-)

Related

Using SWT Accessible Screenreader sometimes ignore my String from getName

Different screen readers (VoiceOver, NVDA, Narrator) ignore my code on different locations of the UI. The method getName(AccessibleEvent e) gets called and supplies the correct result, yet it sometimes is ignored by either VoiceOver, NVDA or Narrator. This behaviour seems so random.
The following case is working with VoiceOver, yet NVDA completely ignores it.
Accessible accessible = getControlForAccesible().getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter() {
#Override
public void getName(AccessibleEvent e) {
e.result = getTextForScreenReader();
}
});
This code is inside a subclass of Composite which acts as button in my case. So there is no subview inside this button. I draw the text with the drawText-method of GC. It also attached a tooltip to the composite which is ignored as well.
No matter which of the methods of the AccessibleAdapter i use, still the same result, whereas on some other parts of my code, those different methods supply different results.
Does anyone had similar problems?

Update GUI components after loading settings JAVA

I have an application with a GUI with sliders and comboboxes mainly. I use the sliders and combos to change values and parameters in various classes in the project. I'm trying to implement the possibility to save and load this parameters and I have done so using the preferences API.
I can save and load parameters, but when I load them I can see the changes in values, so that works, but the GUI does not update to reflect those new values. is there a way to tell the GUI to do so?
For instance I have a frequency slider that changes the frequency value in an oscillator. If I change the value with the slider, save it, change the value of the slider again and then load the saved value, I can hear it has changed but the slider didn't move.(makes sense as I haven't told it too). Can I bind the position of the slider with that frequency value somehow?
gui
//OSC1 slider
JSlider Osc1FreqSlider = new JSlider(SwingConstants.HORIZONTAL, MIN_OSC_FREQ, MAX_OSC_FREQ, NOTE_A_FREQ);
Osc1FreqSlider.setOpaque(false);
Osc1FreqSlider.setMajorTickSpacing(50000);
Osc1FreqSlider.setMinorTickSpacing(50);
Osc1FreqSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
settings.setOsc1Freq((float)source.getValue()/100);
}
});
settings class
public void setOsc1Freq(float freq) {
m_Osc1Freq = freq;
}
After setOsc1Freq( freq ) you have to do jSlider.setValue( freq ).
The way I do this in my own projects is that I never use free-standing primitive values like float freq; instead, I have an observable class with a getter and a setter, which issues an event when the value changes, and then I wrap every single one of my controls in another class which knows how to interact with such an observable. So, every time the value of the control changes, the value in the observable changes too, but also, every time my program changes the value of the observable, the control also takes notice and updates itself too.
I would think that it is practically impossible to write any kind of gui without this functionality, but apparently I seem to be contradicted by the existence of a myriad of gui frameworks out there that do not support this and force you to re-invent.
(Posted on behalf of the OP).
I solved my problem by splitting components declaration, moving them to another singleton class, creating accessor methods and then in the gui class I assigned the variables to the getters.

Add (hover) listener for grid cell after grid first attach

I have something like this.
Grid<Stock> mygrid = new Grid<Stock>(store, cm)
{
#Override
protected void onAfterFirstAttach()
{
super.onAfterFirstAttach();
//here do the binding for the cell elements that have myclass
}
};
And for the view of mygrid I have this
view.setViewConfig(new GridViewConfig<Stock>()
{
#Override
public String getRowStyle(Stock model, int rowIndex)
{
return "";
}
#Override
public String getColStyle(Stock model, ValueProvider<? super Stock, ?> valueProvider, int rowIndex, int colIndex)
{
String style = "";
if(SOME_CASE)
{
style = "myclass";
}
return style;
}
});
What I want to do is onAfterFirstAttach I want to add some listener/handler that during hover will do something, let's say for the sake of simplicity, give an alert.
Is what I'm trying to do possible? Can I accomplish what I want without using JSNI or a library like gwtquery? Is it sure that onAfterFirstAttach all the cells that should have the class myclass will be available and accessible? If yes how can I attach the listener/handler that I want?
The questions seem a lot, but I believe they are all interconnected.
I'm using GWT 2.6 with GXT 3.1 on top of it.
Update
I forgot to mention that a feasible solution would be to add a listener for the mouse move on the whole grid, and then get each cell and check if it has the required class, but this seems such an overkill. There must be a simpler way.
A browser fires native events (click, focus, blur, etc.) regardless of whether you need them. These events "bubble up" the DOM tree giving you a chance to "listen" to them at the right level.
In your example you can create 30 listeners at the cell level, waiting for something to happen in "their" cell. This is a heavy (30 objects) and slow (it takes time to build and bind all of them) solution. Alternatively, you can create a single listener at the grid level, which listens to all events that bubble up from the cells and decides if something needs to be done or an event should be ignored. A single listener is lighter and can be created faster. On the flip side, it needs to examine events, although it's usually a fast and efficient operation (e.g. checking of an event source has a particular class name). Note also that this operation takes time when a browser typically does not have much to do anyway, so examining events only to ignore them does not impact user experience.
In my experience a single listener is almost always a better solution, although modern browsers and computers are so fast that the difference is rarely noticeable.

Java Swing: How to be notified if tool tip is about to become visible?

I have a lot of components that set tool tips with JComponent.setToolTipText(...). However, these tool tips change often based on many events. I could make a setToolTipText(...) call each time one of these events occur; but I would much prefer to simply have a tool tip listener that notifies me when a tip is about to show, so that I can update the tip, if needed. I can't find anyway to assign a listener to the tool tip, am I missing one?
*Note, my solution needs to be Java 1.4.2 compliant.
Actually found a decent solution: override JComponent.getToolTipText().
One disturbing nuance to this, this is the code from JComponent.setToolTipText():
public void setToolTipText(String text) {
String oldText = getToolTipText();
putClientProperty(TOOL_TIP_TEXT_KEY, text);
ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
if (text != null) {
if (oldText == null) {
toolTipManager.registerComponent(this);
}
} else {
toolTipManager.unregisterComponent(this);
}
}
So, if you override getToolTipText to return some dynamic value, it better return null the first time it is called, or your tool tip will not get registered with the ToolTipManager.
I am not sure there is an easy way to be notified of this event. However, it sounds like you might need to change your design. Having to update a large number of components with constantly changing tool tips seems odd and problematic. Maybe just add a generic MouseListener that determines the message for the current component at the current time. That will get you away from having to constantly change all components.
Override the JComponent's createToolTip() this will act as a listener, whenever a tooltip is about to be created, this method is called.

The Elegant way to handle Cyclic Event in Java?

i think this not a specific problem to me; everybody might have encountered this issue before.
To properly illustrate it, here's a simple UI:
As you can see, those two spinners are controlling a single variable -- "A". The only difference is that they control it using different views.
Since these two spinners' displaying values are synchronized, cyclic event shows up.
If i change the top spinner, "A" will be changed and the bottom spinner's value will also be updated accordingly. However, updating the bottom spinner's call (such as setValue) will also trigger another event instructing the top spinner to update based on the bottom spinner's value. Thus creates a bad cycle which can eventually cause a StackOverFlow exception.
My previously solution is kinda cumbersome: i placed a guarding boolean to indicate whether the 2nd updating call should be performed.
Now i'd like to ask "how can i handle such situation elegantly? ( in general, not specific to spinners )"
thx
Update:
Since i've got 2 answers suggesting me to utilize the observer structure, i have to say something about it.
Like what i've said, it's great but far from being perfect. Not only because of its inherent complexity, but also Its inability to solve the problem.
Why? To see the reason, you must realize the tight coupling of the View and Model-Controller in Java Swing. Lets take my spinner UI for an example. Suppose the variable A is actually an Observer object. Then, after firing the first state change event from the top spinner, the Observer "A" will update its value and fire a PropertyChange event to notify the bottom spinner. Then comes the 2nd updating which updates the bottom spinner's View. However, changing bottom spinner's view inevitably triggers a redundant event that will try to set "A"'s value again. Afterwards, the deadly loop is fully constructed and the stack overflow will be thrown.
In theory, the Observer model tries to solve the direct cycle by introducing 2 independent feedback paths. The chained updating odds(in event-response codes) implicitly form a bridge connecting both paths, making a cycle again.
Going back to Model-View-Controller, think about what your Model is, and what your View is.
In your current implementation, you have two models (one for each Spinner control), and they're being synced through the View layer.
What you should be doing though is share the same backing model. For the spinner with a subtracted value, create a proxy to the original model. ie:
class ProxySpinnerModel implements SpinnerModel {
getValue() { return originalSpinner.getValue() - 10 }
setValue(v) { originalSpinner.setValue(v+10) }
}
spinnerA = new JSpinner()
spinnerB = new JSpinner( new ProxySpinnerModel( spinnerA.getModel() ) )
Now, you don't need to add listeners, since they're both working off the same model and the default implementation (the originalModel) already has change listeners which it fires to the view.
Problem Solved
I've got many different suggestions. Particularly,
i want to thank Marc W & Reverend Gonzo. I'm here to make a summary for these ideas; this can save your time navigating thru big chunk of texts.
This problem can be easily bypassed if you carefully decouple the View and Model-Controller.
The dead cycle is caused by dependent writes: write_1 -> write_2 -> write_1 ->.... Intuitively, breaking the dependency can solve the problem elegantly.
If we look into the problem in depth, we can find updating the corresponding views doesn't necessarily involves an external write call. Actually, a view only depends on the data it's representing. Known this, we can then re-write the logic as follow: write_1 -> read_2 & write_2 -> read_1.
To illustrate this idea, lets compare the 3 methods mentioned by different posters:
alt text http://www.freeimagehosting.net/uploads/2707f1b483.png
As you can see, only the proxied view can solve all the dependency thus it's the generic solution for this knid of problem.
In practice, it can be implemented as something like this (in your event-response codes):
setValue(newValue);
anotherSyncUI.parse(); // not anotherSyncUI.setValue() any more
anotherSyncUI.repaint();
No more loops. Solved.
It's a bit complicated, but you could make A actually be an object that's observable. Both spinners (or whatever needs to update itself based on A's value) would then observe A. Whenever A changes, the spinners (or again, whatever object) update themselves to reflect the new value of A. This decouples the spinners' logic from one another. In your example here, the spinners should not be coupled to one another because they really have nothing to do with each other. Instead, they should both simply be bound to A and take care of their own view updating individually.
Whenever the value in the first spinner is changed, you would simply update A's value to match it. Whenever the value in the second spinner is changed, you would of course add 10 to its value before assigning it to A.
Update
In response to the update to your original question, my answer is that the spinners do not listen to one another's change events. Have a separate event handling method for each spinner. A user clicking the up or down arrows in the spinner generates a different event than calling setValue on the spinner programmatically, correct? If the spinners are completely independent of one another, there will be no infinite loop.
E.g. for the second spinner, calculate A-10 and then compare it to the current value of the spinner. If it's the same, do nothing, ending the infinite loop. Similarly for the first spinner.
I think there are also ways to update the spinner's model in a way that doesn't fire an event, but I don't know them off the top of my head.
Use a single SpinnerModel for both JSpinners. See the following code:
Note that the call to setValue() is only made once each time a new value is defined by one of the JSpinners.
import java.awt.BorderLayout;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame jf = new JFrame();
SpinnerModel spinModel = new MySpinnerModel();
JSpinner jspin1 = new JSpinner(spinModel);
JSpinner jspin2 = new JSpinner(spinModel);
jf.setLayout(new BorderLayout());
jf.add(jspin1, BorderLayout.NORTH);
jf.add(jspin2, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(3);
}
}
class MySpinnerModel extends AbstractSpinnerModel {
private int _value = 0;
private int _min = 0;
private int _max = 10;
#Override
public Object getNextValue() {
if (_value == _max) {
return null;
}
return _value + 1;
}
#Override
public Object getPreviousValue() {
if (_value == _min) {
return null;
}
return _value - 1;
}
#Override
public Object getValue() {
return _value;
}
#Override
public void setValue(Object value) {
System.out.println("setValue(" + value + ")");
if (value instanceof Integer) {
_value = (Integer) value;
fireStateChanged();
}
}
}
It seems you're really observing the wrong thing. From the example given I presume what you want to detect is the user's actions on the controls, not the changes in the values themselves. As you've outlined, changes in your model are reflected in the values of the spinners, and it is this which forms the infinite loop of events.
However, diving further into the UI implementation may not be the answer you want. In that case I'd say the best you can do is either your current guard solution, or to better extract the logic into your model (similar to what Marc and William have said). How that can be done will depend on the 'real world' model behind a particular implementation of the provided puzzle.
As a rule, your model should not be defined by your GUI. Ie, the SpinnerModel that backs each JSpinner should not be your value A. (That would be a horribly inelegant tightly coupled dependency on a particular view.)
Instead, your value A should either be a POJO or a property of an object. In which case, you can add PropertyChangeSupport to it. (And presumably have already done so in any case, as you want your spinners to update automatically if A is changed by other parts of your program).
I realise this is similar to Marc W's answer, and you were concerned that it's "complicated", but PropertyChangeSupport does almost all of it for you.
In fact, for trivially simple cases, you can just use a single class that wires a "setProperty" method through to a "firePropertyChange" call (as well as storing the value in a HashMap for any "getProperty" calls).
I don't really want to solve your problem but I find it interesting. I have already been confront to it and solved it each time a different way. But when I think about the 'why ?' and not about the 'how ?' am staying perplexed.
This problem only exists because I am using an automatism (MVC) which had to help me, and exactly in that way. The art how the components are used make this automatism a barrier to a beautiful code.
Why do set #setEvent() has to produce the same event as a GUI action?
Though, my opinion is also pretty close to Observer pattern but it is a bit lighter than that!!!
Have A as a variable with a setter
private Integer A;
setA(int A)
{
this.A = A;
refreshSpinners();
}
refreshSpinners()
{
setSpinnerA();
setSpinnerAMinus10();
}
setSpinnerA()
{
// show value of A
}
setSpinnerAMinus10()
{
// show value of A-10
}

Categories

Resources