All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.gui.StandardAlgConfigPanel Maven / Gradle / Ivy

/*
 * Copyright (c) 2022, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.gui;

import boofcv.gui.controls.JCheckBoxValue;
import boofcv.gui.controls.JConfigLength;
import boofcv.gui.controls.JSpinnerNumber;
import boofcv.misc.BoofLambdas;
import boofcv.struct.ConfigLength;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;

/**
 * Common base class for panels used for configuring the algorithms.
 *
 * @author Peter Abeles
 */
@SuppressWarnings({"unused", "SameParameterValue"})
public class StandardAlgConfigPanel extends JPanel implements ActionListener, ChangeListener, JConfigLength.Listener {

	public StandardAlgConfigPanel() {
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
	}

	public JFormattedTextField textfield( double value, double min, double max, int panelWidth ) {
		JFormattedTextField field = BoofSwingUtil.createTextField(value, min, max);
		field.addActionListener(this);
		field.setPreferredSize(new Dimension(panelWidth, 24));
		field.setMaximumSize(field.getPreferredSize());
		return field;
	}

	/**
	 * Wraps the input panel inside another panel that will fill the control panel horizontally
	 */
	public static JPanel fillHorizontally( JPanel panel ) {
		JPanel hack = new JPanel(new BorderLayout()) {
			@Override
			public Dimension getPreferredSize() {
				Dimension p = super.getPreferredSize();
				p.width = Short.MAX_VALUE;
				return p;
			}

			@Override
			public Dimension getMaximumSize() {
				return getPreferredSize();
			}
		};
		hack.add(panel, BorderLayout.CENTER);
		return hack;
	}

	protected void addToolTip( JComponent component, String toolTip ) {
		component.setToolTipText(toolTip);
		add(component);
	}

	public JConfigLength configLength( ConfigLength initial, double min, double max ) {
		boolean integerMode = ((int)initial.length) == initial.length;
		JConfigLength control = new JConfigLength(this, integerMode);
		control.setValue(initial);
		control.setLengthBounds(min, max);
		control.setMaximumSize(control.getPreferredSize());
		return control;
	}

	public JConfigLength configLength( ConfigLength initial, double min, double max, BoofLambdas.ProcessCall listener ) {
		boolean integerMode = ((int)initial.length) == initial.length;
		JConfigLength.Listener cl = ( source, fraction, length ) -> listener.process();

		JConfigLength control = new JConfigLength(cl, integerMode);
		control.setValue(initial);
		control.setLengthBounds(min, max);
		control.setMaximumSize(control.getPreferredSize());
		return control;
	}

	public JButton buttonIcon( String name, boolean enabled ) {
		JButton b = BoofSwingUtil.createButtonIconGUI(name, 24, 24);
		b.setEnabled(enabled);
		b.addActionListener(this);
		return b;
	}

	public JButton button( String name, boolean enabled ) {
		return button(name, enabled, this);
	}

	public JButton button( String name, boolean enabled, ActionListener listener ) {
		JButton b = new JButton(name);
		b.setEnabled(enabled);
		b.addActionListener(listener);
		return b;
	}

	public JSlider slider( int min, int max, int initial, int widgetWidth ) {
		JSlider slider = new JSlider(min, max, initial);
		slider.setMaximumSize(new Dimension(widgetWidth, 24));
		slider.setPreferredSize(slider.getMaximumSize());
		slider.addChangeListener(this);
		return slider;
	}

	public JComboBox combo( int initial, Object... items ) {
		JComboBox c = new JComboBox<>();

		for (int i = 0; i < items.length; i++) {
			c.addItem(items[i].toString());
		}
		c.setSelectedIndex(initial);
		c.addActionListener(this);
		c.setMaximumSize(c.getPreferredSize());
		return c;
	}

	public JComboBox combo( ActionListener listener, int initial, Object... items ) {
		JComboBox c = new JComboBox<>();

		for (int i = 0; i < items.length; i++) {
			c.addItem(items[i].toString());
		}
		c.setSelectedIndex(initial);
		if (listener != null)
			c.addActionListener(listener);
		c.setMaximumSize(c.getPreferredSize());
		return c;
	}

	public JSpinner spinner( int initial, Object[] items ) {
		JSpinner spinner = new JSpinner(new SpinnerListModel(items));
		spinner.setValue(items[initial]);
		spinner.setMaximumSize(spinner.getPreferredSize());
		spinner.addChangeListener(this);
		return spinner;
	}

	public JSpinner spinner( int initial, int minimum, int maximum, int stepSize, ChangeListener listener ) {
		JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		spinner.setMaximumSize(spinner.getPreferredSize());
		spinner.addChangeListener(listener);
		return spinner;
	}

	public JSpinner spinner( int initial, int minimum, int maximum, int stepSize ) {
		JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		spinner.setMaximumSize(spinner.getPreferredSize());
		spinner.addChangeListener(this);
		return spinner;
	}

	public JSpinner spinner( double initial, double minimum, double maximum, double stepSize ) {
		JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		spinner.setMaximumSize(spinner.getPreferredSize());
		spinner.addChangeListener(this);
		return spinner;
	}

	public JSpinner spinner( double initial, double minimum, double maximum, double stepSize,
							 int integerDigits, int fractionDigits ) {
		JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		JSpinner.NumberEditor editor = (JSpinner.NumberEditor)spinner.getEditor();
		DecimalFormat format = editor.getFormat();
		format.setMinimumFractionDigits(fractionDigits);
		format.setMinimumIntegerDigits(integerDigits);
		editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER);
		Dimension d = spinner.getPreferredSize();
		d.width = (integerDigits + 1 + fractionDigits)*12;
		spinner.setPreferredSize(d);
		spinner.setMaximumSize(d);
		// force it to render using the format specified above. A bit of a hack. Got a better idea?
		spinner.setValue(1 - initial);
		spinner.setValue(initial);
		spinner.addChangeListener(this);
		return spinner;
	}

	public JSpinnerNumber spinnerWrap( double initial, double minimum, double maximum, double stepSize ) {
		var spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		spinner.setMaximumSize(spinner.getPreferredSize());
		var wrap = new JSpinnerNumber(spinner, initial);
		spinner.addChangeListener(( e ) -> {
			wrap.updateValue();
			stateChanged(e);
		});
		return wrap;
	}

	public JSpinnerNumber spinnerWrap( int initial, int minimum, int maximum, int stepSize ) {
		var spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		spinner.setMaximumSize(spinner.getPreferredSize());
		var wrap = new JSpinnerNumber(spinner, initial);
		spinner.addChangeListener(( e ) -> {
			wrap.updateValue();
			stateChanged(e);
		});
		return wrap;
	}

	/**
	 * Creates a spiner for double data type.
	 *
	 * @param formatString Example of a format. "0.0E0". See {@link DecimalFormat}
	 */
	public JSpinner spinner( double initial, double minimum, double maximum, double stepSize,
							 String formatString, int digits ) {

		JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial, minimum, maximum, stepSize));
		JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, formatString);
		spinner.setEditor(editor);
		editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER);
		Dimension d = spinner.getPreferredSize();
		d.width = digits*9;
		spinner.setPreferredSize(d);
		spinner.setMaximumSize(d);
		// force it to render using the format specified above. A bit of a hack. Got a better idea?
		spinner.setValue(1 - initial);
		spinner.setValue(initial);
		spinner.addChangeListener(this);
		return spinner;
	}

	public void configureSpinnerFloat( JSpinner spinner, int integerDigits, int fractionDigits ) {
		double min = ((Number)((SpinnerNumberModel)spinner.getModel()).getMinimum()).doubleValue();
		int adjust = min < 0 ? 1 : 0;
		JSpinner.NumberEditor editor = (JSpinner.NumberEditor)spinner.getEditor();
		DecimalFormat format = editor.getFormat();
		format.setMinimumFractionDigits(fractionDigits);
		format.setMinimumIntegerDigits(integerDigits);
		editor.getTextField().setHorizontalAlignment(SwingConstants.CENTER);
		Dimension d = spinner.getPreferredSize();
		d.width = (adjust + integerDigits + 1 + fractionDigits)*11;
		spinner.setPreferredSize(d);
		spinner.setMaximumSize(d);
	}

	public JCheckBox checkbox( String text, boolean value ) {
		return checkbox(text, value, null);
	}

	public JCheckBox checkbox( String text, boolean value, @Nullable String tooltip ) {
		JCheckBox c = new JCheckBox(text);
		c.setSelected(value);
		c.addActionListener(this);

		if (tooltip != null)
			c.setToolTipText(tooltip);

		return c;
	}

	public JRadioButton radio( String text, boolean value, @Nullable String tooltip ) {
		var c = new JRadioButton(text);
		c.setSelected(value);
		c.addActionListener(this);

		if (tooltip != null)
			c.setToolTipText(tooltip);

		return c;
	}

	/**
	 * Creates a {@link ButtonGroup} for the buttons
	 */
	public static void group(AbstractButton ...buttons) {
		var buttonGroup = new ButtonGroup();
		for (AbstractButton b : buttons) {
			buttonGroup.add(b);
		}
	}

	public JCheckBoxValue checkboxWrap( String text, boolean value ) {
		JCheckBox c = new JCheckBox(text);
		c.setSelected(value);
		var wrap = new JCheckBoxValue(c, value);
		// update the value automatically, this is almost always done
		c.addActionListener(( e ) -> {
			wrap.updateValue();
			actionPerformed(e);
		});
		return wrap;
	}

	public void addAlignLeft( JComponent target, String tooltip ) {
		target.setToolTipText(tooltip);
		addAlignLeft(target, this);
	}

	public void addAlignRight( JComponent target, String tooltip ) {
		target.setToolTipText(tooltip);
		addAlignRight(target, this);
	}

	public void addAlignCenter( JComponent target, String tooltip ) {
		target.setToolTipText(tooltip);
		addAlignCenter(target, this);
	}

	public void addAlignLeft( JComponent target ) {
		addAlignLeft(target, this);
	}

	public void addAlignRight( JComponent target ) {
		addAlignRight(target, this);
	}

	public void addAlignCenter( JComponent target ) {
		addAlignCenter(target, this);
	}

	public static void addAlignLeft( JComponent target, JPanel owner ) {
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
		p.add(target);
		p.add(Box.createHorizontalGlue());
		owner.add(p);
	}

	public static void addAlignRight( JComponent target, JPanel owner ) {
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
		p.add(Box.createHorizontalGlue());
		p.add(target);
		owner.add(p);
	}

	public static void addAlignCenter( JComponent target, JPanel owner ) {
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
		p.add(Box.createHorizontalGlue());
		p.add(target);
		p.add(Box.createHorizontalGlue());
		owner.add(p);
	}

	public void addCenterLabel( String text ) {
		addCenterLabel(text, this);
	}

	public void addCenterLabel( String text, JPanel owner ) {
		JLabel l = new JLabel(text);
		l.setAlignmentX(Component.CENTER_ALIGNMENT);
		owner.add(l);
		owner.add(Box.createRigidArea(new Dimension(1, 8)));
	}

	public void addSeparator( int width ) {
		add(Box.createRigidArea(new Dimension(1, 8)));
		JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL);
		separator.setMaximumSize(new Dimension(width, 5));
		add(separator);
		add(Box.createRigidArea(new Dimension(1, 8)));
	}

	public void addSeparator() {
		add(Box.createRigidArea(new Dimension(1, 8)));
		add(new JSeparator());
		add(Box.createRigidArea(new Dimension(1, 8)));
	}

	public void addLabeled( JComponent target, String text ) {
		addLabeled(target, text, null);
	}

	public void addLabeled( JComponent target, String text, @Nullable String tooltip ) {
		addLabeled(target, text, tooltip, this);
	}

	public static void addLabeled( JComponent target, String text, @Nullable String tooltip, JPanel owner ) {
		JLabel label = new JLabel(text);
		label.setLabelFor(target);
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
		p.add(label);
		p.add(Box.createHorizontalGlue());
		p.add(target);
		owner.add(p);

		if (tooltip != null) {
			label.setToolTipText(tooltip);
			target.setToolTipText(tooltip);
		}
	}

	public static JPanel createHorizontalPanel( Component... children ) {
		JPanel panel = new JPanel();
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
		for (var c : children) {
			panel.add(c);
		}
		return panel;
	}

	public void addLabeledV( JComponent target, String text ) {
		addLabeledV(target, text, this);
	}

	public static void addLabeledV( JComponent target, String text, JPanel owner ) {
		JLabel label = new JLabel(text);
		label.setLabelFor(target);
		target.setAlignmentX(Component.CENTER_ALIGNMENT);
		label.setAlignmentX(Component.CENTER_ALIGNMENT);
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
		p.add(label);
		p.add(target);
		owner.add(p);
	}

	public void addVerticalGlue() {
		addVerticalGlue(this);
	}

	public static void addVerticalGlue( JPanel owner ) {
		owner.add(Box.createVerticalGlue());
	}

	public void addHorizontalGlue( JPanel owner ) {
		owner.add(Box.createHorizontalGlue());
	}

	protected void setEnabled( int index, boolean enabled ) {
		Component c = getComponent(index);
		if (c instanceof JPanel) {
			JPanel p = (JPanel)c;
			p.getComponent(0).setEnabled(enabled);
			p.getComponent(2).setEnabled(enabled);
		} else if (c instanceof JLabel) {
			c.setEnabled(enabled);
		}
	}

	/**
	 * Searches inside the children of "root" for a component that's a JPanel. Then inside the JPanel it
	 * looks for the target. If the target is inside the JPanel the JPanel is removed from root.
	 */
	protected static void removeChildInsidePanel( JComponent root, JComponent target ) {
		int N = root.getComponentCount();

		for (int i = 0; i < N; i++) {
			try {
				JPanel p = (JPanel)root.getComponent(i);
				Component[] children = p.getComponents();
				for (int j = 0; j < children.length; j++) {
					if (children[j] == target) {
						root.remove(i);
						return;
					}
				}
			} catch (ClassCastException ignore) {
			}
		}
	}

	/**
	 * Searches inside the children of "root" for 'target'. If found it is removed and the
	 * previous component.
	 */
	protected static void removeChildAndPrevious( JComponent root, JComponent target ) {
		int N = root.getComponentCount();

		for (int i = 0; i < N; i++) {
			if (root.getComponent(i) == target) {
				root.remove(i);
				root.remove(i - 1);
				return;
			}
		}
		throw new RuntimeException("Can't find component");
	}

	@Override
	public void actionPerformed( ActionEvent e ) {
		controlChanged(e.getSource());
	}

	@Override
	public void stateChanged( ChangeEvent e ) {
		controlChanged(e.getSource());
	}

	@Override
	public void changeConfigLength( JConfigLength source, double fraction, double length ) {
		controlChanged(source);
	}

	/**
	 * In almost all situations we just need to know that the state of a control has changed. No need to implement
	 * seperate listeners for all
	 */
	public void controlChanged( Object source ) {
		throw new RuntimeException("You need to override controlChanges() or implement actionPerformed()/stateChanged()");
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy