📄 input.java
字号:
// (c) 2003 Allen I Holub. All rights reserved.
package com.holub.ui;
import com.holub.ui.NumericInput;// for testing
import java.util.logging.*;
import javax.swing.*;
import javax.swing.Timer; // disambiguate from java.util.Timer
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import com.holub.tools.Log;
/* Demonstrate a JComponent-style proxy.
* <p>
* This class is
* a validating Text field. The first time you type something
* invalid, an tool tip pops up describing what correct
* input is. Subsequent errors just beep at you, but the
* tooltip will continue to pop up if the mouse hovers.
*
* <!-- ====================== distribution terms ===================== -->
* <p><blockquote
* style="border-style: solid; border-width:thin; padding: 1em 1em 1em 1em;">
* <center>
* Copyright © 2003, Allen I. Holub. All rights reserved.
* </center>
* <br>
* <br>
* This code is distributed under the terms of the
* <a href="http://www.gnu.org/licenses/gpl.html"
* >GNU Public License</a> (GPL)
* with the following ammendment to section 2.c:
* <p>
* As a requirement for distributing this code, your splash screen,
* about box, or equivalent must include an my name, copyright,
* <em>and URL</em>. An acceptable message would be:
* <center>
* This program contains Allen Holub's <em>XXX</em> utility.<br>
* (c) 2003 Allen I. Holub. All Rights Reserved.<br>
* http://www.holub.com<br>
* </center>
* If your progam does not run interactively, then the foregoing
* notice must appear in your documentation.
* </blockquote>
* <!-- =============================================================== -->
* @see DateInput
* @see NumericInput
* @author Allen I. Holub
*/
public class Input extends JTextField implements Styles
{
private final Customizer customizer;
private String lastValid; // Useful only in on-exit-style validation.
// holds the last-known valid contents
// of the control.
private boolean valid = true; // Used for communication between
private int offset; // the Document event handler and the
private int length; // UI Delegate update code.
private Popup popup = null; // Popup window used for "help,"
// is null unless window is visible.
private static int POPUP_LIFETIME = 15; // Maximum lifetime of popup
// window in seconds.
/** Provides information about, defines nonstandard initial
* state for, and validates user input.
* The predefined {@link NumericInput} class implements
* this interface for generic numbers, and the predefined
* {@link Input.Default} class implements it for
* a default, non-validating text control.
*/
public interface Customizer
{
/** Called by the Input object to determine whether validation
* is performed (by calling {@link #isValid}) after every
* character is entered or when the user hits enter (or the
* input object looses focus)
* <p>
* Note that on-exit validation causes validation to occur
* in two situations: the user hits Enter or the control
* looses focus. The Control won't let the focus change
* occur if the contents don't validate. However the Esc
* character is recognized as a reset-to-original-value
* request, so you can exit the field if you want to.
* The string ("Type Esc to exit") is automatically appended
* to the error message in this mode.
*
* @return false for character-by-character validation, true for
* one-time validation on loss of focus or Enter.
*/
boolean validatesOnExit();
/** Return true if the string is valid input. This method
* is called either on exiting the control or after every
* character is typed, depending on the return value of
* {@link #validatesOnExit}. Reguardless of the
* "validate-on-exit" mode, an hitting Esc alwasy resets
* the control to the last-known valid value.
* <p>
* This method is also called when the control is initialized,
* and the constructor will throw an exception if the initial
* value is not valid.
*
* @param s The entire contents of the control, including any
* characters the user just entered.
*/
boolean isValid( String s );
/** Return a description of what valid input looks likes.
* The string should be HTML, however the main context
* <html>, <head>, and <body> elements are already
* established, so these tags should not appear in
* your own text.
* @param badInput This is the string that the user tried to type.
* The control rejects the bad input, so this
* string is not displayed. You can put it into
* your error message if you like, however.
* @return a help string or null if no help is avilable.
*/
String help( String badInput );
/** Set up the look of the component for stuff not covered
* by the Style class. (For example, alignment and tool-tip
* text). This method after all initializations (including
* the text entry) have been made to the component.
* customizations have been made.
*/
void prepare( JTextField current );
}
/** An implemenation of Customizer that defines default behavior:
* All input is valid; there is no help; The prepare
* method makes the control 30 columns wide.
* You can extend this class if you just want
* to override one of the methods, much like an
* AWT <em>Xxx</em><code>Adapter</code>.
*/
static public class Default implements Customizer
{ public boolean isValid(String s){return true;}
public String help(String s) { return null; }
public void prepare( JTextField current )
{ current.setColumns(30);
}
public boolean validatesOnExit(){ return true; }
};
/** A constrained type to describe the border style you want.
* One of the predefined instances Input.BOXED, Input.UNDERLINED,
* or Input.BORDERLESS, must be passed to the
* {@linkplain Input#Input <code>Input</code> constructor}.
*/
public static final class BorderStyle{ private BorderStyle(){} }
public static final BorderStyle BORDERLESS = null;
public static final BorderStyle BOXED = new BorderStyle();
public static final BorderStyle UNDERLINED = new BorderStyle();
/** Construct a validating input field. Works like a JTextField,
* but checks the input as it's typed and complains
* if the input is invalid. Also implements the TagBehavior
* interface, so can be used in a PAC system as a stand-in
* for an <input> tag.
* <p>
* The default width of the control is determined by the width of the
* <code>value</code> string. You can change the width from the
* default by calling {@link javax.swing.JTextField#setColumns} after
* you create the object. Unlike the standard JTextField, the maximum
* and minimum widths are constrained to the initial size. (This
* constraint is required for the control to work correctly inside
* a {@link com.holub.ui.HTML.HtmlPane}, which is it's raison d'etre.)
*
* @param value initial value.
* @param customizer checks to see if the input is valid. A null argument
* is treated as if you had passed an {@link Default} object.
* @param border one of BORDERLESS, BOXED, or UNDERLINED.
* @param isHighlighted if true, highlight the text in the Style.HIGHLIGHT_COLOR
*
* @throws IllegalArgumentException if the customizer indicates that the
* initial <code>value</code> is invalid.
*/
public Input(String value, Customizer customizer, final BorderStyle border, final boolean isHighlighted)
{
this.customizer = (customizer != null)? customizer: new Default();
if( !customizer.isValid(value) )
throw new IllegalArgumentException("Customizer rejected initial value ["+ value +"]");
lastValid = value;
setFont( FONT );
if( isHighlighted )
setForeground( Color.RED ); // Affects border color too.
if( border==BOXED )
{ Border outer = BorderFactory.createLineBorder( Color.BLACK, 1 );
Border inner = BorderFactory.createEmptyBorder( 0, 4, 0, 4 );
setBorder( BorderFactory.createCompoundBorder(outer, inner) );
}
else if( border==UNDERLINED )
{ setBorder
( new AbstractBorder()
{ public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height)
{ g.drawLine( x, y+height-1, x+width-1, y+height-1 );
}
}
);
}
else // BORDERLESS
{ setBorder( null ); // no border
}
setColumns( value.length() );
setText( value );
customizer.prepare( this );
// We do our own action-listener handling here, because
// notifications are sent on loss of focus as well as
// Enter. checkOnExit() validates the data, and
// notifies listeners if its valid.
super.addActionListener // handles Enter
( new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if( !validateInputAndNotifyListenersIfValid() )
retainFocus();
}
}
);
// Send action events on loss of focus as well as Enter, but
// only if the control holds valid input.
super.addFocusListener
( new FocusAdapter()
{ public void focusLost( FocusEvent e )
{ if( !validateInputAndNotifyListenersIfValid() )
retainFocus();
}
}
);
// Process an Esc to reset the field to its last known good
// value.
super.addKeyListener
( new KeyAdapter()
{ public void keyPressed( KeyEvent e )
{ if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
{ Input.this.setText( lastValid );
}
}
}
);
// Set up a document listener. The documenation for JTextField
// uses an insertString override to map characters to upper case,
// but here, I want to disallow characters that will make the
// entire string invalid. Consequently, a different approach is
// required. The characterValidator is always installed because
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -