Java tutorial
/* Copyright (c) 2013 Jesper qvist <jesper@llbit.se> * * This file is part of Chunky. * * Chunky is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Chunky is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Chunky. If not, see <http://www.gnu.org/licenses/>. */ package se.llbit.chunky.renderer.ui; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Group; import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.math3.util.FastMath; import se.llbit.math.QuickMath; /** * Adjusts a rendering parameter. * * @author Jesper qvist <jesper@llbit.se> */ public abstract class Adjuster implements ChangeListener, ActionListener { private final JLabel lbl; private final JSlider slider; private final JTextField textField; private double min; private double max; private final boolean integerMode; /** * logarithmic slider */ private boolean logarithmic = false; /** * Clamp values to minimum */ private boolean clampMin = true; /** * Clamp values to maximum */ private boolean clampMax = true; /** * Number format for current locale. */ private static final NumberFormat numberFormat = NumberFormat.getInstance(); /** * Create new double value adjuster * @param label * @param tip * @param min * @param max */ public Adjuster(String label, String tip, double min, double max) { this.min = min; this.max = max; lbl = new JLabel(label + ":"); slider = new JSlider(1, 100); slider.setToolTipText(tip); slider.addChangeListener(this); textField = new JTextField(5); textField.addActionListener(this); integerMode = false; } /** * Create new integer value adjuster * @param label * @param tip * @param min * @param max */ public Adjuster(String label, String tip, int min, int max) { this.min = min; this.max = max; lbl = new JLabel(label); slider = new JSlider(min, max); slider.setToolTipText(tip); slider.addChangeListener(this); textField = new JTextField(5); textField.addActionListener(this); integerMode = true; } /** * Set logarithmic mode for the slider * @param mode */ public void setLogarithmicMode(boolean mode) { logarithmic = mode; } /** * Select clamping mode * @param mode <code>true</code> means clamping is enabled */ public void setClampMin(boolean mode) { clampMin = mode; } /** * Select clamping mode * @param mode <code>true</code> means clamping is enabled */ public void setClampMax(boolean mode) { clampMax = mode; } /** * @param layout * @return horizontal layout group */ public Group horizontalGroup(GroupLayout layout) { return layout.createSequentialGroup().addComponent(lbl).addPreferredGap(ComponentPlacement.RELATED) .addComponent(slider).addPreferredGap(ComponentPlacement.RELATED).addComponent(textField); } /** * @param layout * @return vertical layout group */ public Group verticalGroup(GroupLayout layout) { return layout.createParallelGroup().addComponent(lbl).addComponent(slider).addComponent(textField, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE); } @Override public void actionPerformed(ActionEvent e) { JTextField source = (JTextField) e.getSource(); try { double value = numberFormat.parse(source.getText()).doubleValue(); double sliderValue = QuickMath.clamp(value, min, max); if (clampMin) { value = QuickMath.max(value, min); } if (clampMax) { value = QuickMath.min(value, max); } setSlider(sliderValue); valueChanged(value); } catch (NumberFormatException ex) { } catch (ParseException ex) { // TODO warn user that the value was not updated } } @Override public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); double value; if (logarithmic) { value = (double) (source.getValue() - source.getMinimum()) / (source.getMaximum() - source.getMinimum()); double logMin = FastMath.log10(min); double logMax = FastMath.log10(max); double scale = logMax - logMin; value = FastMath.pow(10, value * scale + logMin); } else { double scale = (max - min) / (source.getMaximum() - source.getMinimum()); value = (source.getValue() - source.getMinimum()) * scale + min; } setTextField(value); valueChanged(value); } /** * Set the parameter value * @param value */ public void set(double value) { setSlider(value); setTextField(value); } /** * Set parameter value and new min/max limits * @param value * @param min New minimum value * @param max New maximum value */ public void set(double value, double min, double max) { this.min = min; this.max = max; setSlider(value); setTextField(value); } private void setSlider(double value) { int sliderValue; if (logarithmic) { double logMin = FastMath.log10(min); double logMax = FastMath.log10(max); double logValue = (FastMath.log10(value) - logMin) / (logMax - logMin); double scale = slider.getMaximum() - slider.getMinimum(); sliderValue = (int) (logValue * scale + slider.getMinimum()); } else { double scale = (slider.getMaximum() - slider.getMinimum()) / (max - min); sliderValue = (int) ((value - min) * scale + 0.5 + slider.getMinimum()); } setSliderValue(sliderValue); } protected void setSliderValue(int value) { slider.removeChangeListener(this); slider.setValue(value); slider.addChangeListener(this); } /** * Update the text field * @param value new value */ private void setTextField(double value) { if (integerMode) { setTextFieldText("" + (int) value); } else { setTextFieldText(String.format("%.2f", value)); } } protected void setTextFieldText(String text) { textField.removeActionListener(this); textField.setText(text); textField.addActionListener(this); } /** * Handles value changes * @param newValue */ public abstract void valueChanged(double newValue); /** * Update the adjuster with the current value */ public abstract void update(); /** * @return The label */ public JLabel getLabel() { return lbl; } /** * @return The slider */ public JSlider getSlider() { return slider; } /** * @return The text field */ public JTextField getField() { return textField; } /** * Enable or disable the controls of this adjuster * @param enabled */ public void setEnabled(boolean enabled) { lbl.setEnabled(enabled); slider.setEnabled(enabled); textField.setEnabled(enabled); } }