Context
Im writing a piece of code that has 1+ Sliders. These sliders form a group. The sum of the slider values of this group must always be 100. However, as a NEEDED feature, the user can disable and enable (add/remove) sliders from this group. Therefore, this requires that the sliders values get adjusted properly.
After attempting to code my own group, I decided to look for a better/tested and implemented code. It improved from mine, however, some issues appeared.
Primary Issue
Adding or removing sliders by selection or deselection the checkbox causes errors and the sliders stop working. Notice that adding, in this scenario, just means enabling a previously disabled slider (by deselecting the checkbox).
Preliminary Solution
The code below was found in stackoverflow. I did implement in my code but since I cant post it, I decided to adjust the code found in the stackoverflow example to represent my scenario.
How to?
Any help is greatly appreciated. Im not sure how to approach the fix for this problem without causing more errors. I did try re-adjusting the calculus done on the update method but it just caused more nonsense. I dont find productive posting all my attempts here because stackoverflow would say its too much code and because im not sure it would help with finding an answer.
References
https://stackoverflow.com/a/21391448/2280645
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ConnectedSliders {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JSlider s0 = new JSlider(0, 100, 30);
JSlider s1 = new JSlider(0, 100, 40);
JSlider s2 = new JSlider(0, 100, 30);
SliderGroup sliderGroup = new SliderGroup();
//sliderGroup.add(s0);
//sliderGroup.add(s1);
//sliderGroup.add(s2);
JPanel panel = new JPanel(new GridLayout(0, 3));
panel.add(s0);
panel.add(createListeningLabel(s0));
panel.add(createCheckBox(s0, sliderGroup));
panel.add(s1);
panel.add(createListeningLabel(s1));
panel.add(createCheckBox(s1, sliderGroup));
panel.add(s2);
panel.add(createListeningLabel(s2));
panel.add(createCheckBox(s2, sliderGroup));
panel.add(createListeningLabel(s0, s1, s2));
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JLabel createListeningLabel(final JSlider... sliders) {
final JLabel label = new JLabel("");
for (JSlider slider : sliders) {
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int sum = 0;
for (JSlider slider : sliders) {
sum += slider.getValue();
}
label.setText("Sum: " + sum);
}
});
}
return label;
}
private static JLabel createListeningLabel(final JSlider slider) {
final JLabel label = new JLabel("");
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
label.setText(String.valueOf(slider.getValue()));
}
});
return label;
}
private static JCheckBox createCheckBox(final JSlider slider, SliderGroup group) {
final JCheckBox checkBox = new JCheckBox();
checkBox.setSelected(true);
checkBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
group.add(slider);
slider.setEnabled(true);
} else if(e.getStateChange() == ItemEvent.DESELECTED) {
group.remove(slider);
slider.setEnabled(false);
}
}
});
return checkBox;
}
}
class SliderGroup {
private final Map<JSlider, Integer> values;
private final LinkedList<JSlider> candidates;
private final ChangeListener changeListener;
private boolean updating = false;
SliderGroup() {
this.values = new HashMap<JSlider, Integer>();
this.candidates = new LinkedList<JSlider>();
changeListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
update(source);
}
};
}
private void update(JSlider source) {
if (updating) {
return;
}
updating = true;
int delta = source.getValue() - values.get(source);
if (delta > 0) {
distributeRemove(delta, source);
} else {
distributeAdd(delta, source);
}
for (JSlider slider : candidates) {
values.put(slider, slider.getValue());
}
updating = false;
}
private void distributeRemove(int delta, JSlider source) {
int counter = 0;
int remaining = delta;
while (remaining > 0) {
JSlider slider = candidates.removeFirst();
counter++;
if (slider == source) {
candidates.addLast(slider);
} else {
if (slider.getValue() > 0) {
slider.setValue(slider.getValue() - 1);
remaining--;
counter = 0;
}
candidates.addLast(slider);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String message = "Can not distribute " + delta + " among " + candidates;
//System.out.println(message);
//return;
throw new IllegalArgumentException(message);
}
}
}
private void distributeAdd(int delta, JSlider source) {
int counter = 0;
int remaining = -delta;
while (remaining > 0) {
JSlider slider = candidates.removeLast();
counter++;
if (slider == source) {
candidates.addFirst(slider);
} else {
if (slider.getValue() < slider.getMaximum()) {
slider.setValue(slider.getValue() + 1);
remaining--;
counter = 0;
}
candidates.addFirst(slider);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String message = "Can not distribute " + delta + " among " + candidates;
//System.out.println(message);
//return;
throw new IllegalArgumentException(message);
}
}
}
void add(JSlider slider) {
candidates.add(slider);
values.put(slider, slider.getValue());
slider.addChangeListener(changeListener);
}
void remove(JSlider slider) {
candidates.remove(slider);
values.remove(slider);
slider.removeChangeListener(changeListener);
}
}