Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.control;
027
028import javafx.css.PseudoClass;
029import javafx.beans.property.*;
030import javafx.event.ActionEvent;
031import javafx.event.Event;
032import javafx.event.EventHandler;
033import javafx.event.EventType;
034
035/**
036 * Abstract base class for ComboBox-like controls. A ComboBox typically has
037 * a button that, when clicked, will pop up some means of allowing a user
038 * to select one or more values (depending on the implementation). This base
039 * class makes no assumptions about what happens when the {@link #show()} and
040 * {@link #hide()} methods are called, however commonly this results in either
041 * a popup or dialog appearing that allows for the user to provide the 
042 * required information.
043 * 
044 * <p>A ComboBox has a {@link #valueProperty() value} property that represents
045 * the current user input. This may be based on a selection from a drop-down list,
046 * or it may be from user input when the ComboBox is 
047 * {@link #editableProperty() editable}.
048 * 
049 * <p>An {@link #editableProperty() editable} ComboBox is one which provides some
050 * means for an end-user to provide input for values that are not otherwise
051 * options available to them. For example, in the {@link ComboBox} implementation,
052 * an editable ComboBox provides a {@link TextField} that may be typed into.
053 * As mentioned above, when the user commits textual input into the textfield
054 * (commonly by pressing the Enter keyboard key), the 
055 * {@link #valueProperty() value} property will be updated.
056 * 
057 * <p>The purpose of the separation between this class and, say, {@link ComboBox} 
058 * is to allow for ComboBox-like controls that do not necessarily pop up a list 
059 * of items. Examples of other implementations include color pickers, calendar 
060 * pickers, etc. The  {@link ComboBox} class provides the default, and most commonly
061 * expected implementation. Refer to that classes javadoc for more information.
062 * 
063 * @see ComboBox
064 * @param <T> The type of the value that has been selected or otherwise
065 *      entered in to this ComboBox.
066 */
067public abstract class ComboBoxBase<T> extends Control {
068    
069 
070    /***************************************************************************
071     *                                                                         *
072     * Static properties and methods                                           *
073     *                                                                         *
074     **************************************************************************/
075    
076    /**
077     * <p>Called prior to the ComboBox showing its popup/display after the user
078     * has clicked or otherwise interacted with the ComboBox.
079     */
080    public static final EventType<Event> ON_SHOWING =
081            new EventType<Event>(Event.ANY, "COMBO_BOX_BASE_ON_SHOWING");
082
083    /**
084     * <p>Called after the ComboBox has shown its popup/display.
085     */
086    public static final EventType<Event> ON_SHOWN =
087            new EventType<Event>(Event.ANY, "COMBO_BOX_BASE_ON_SHOWN");
088
089    /**
090     * <p>Called when the ComboBox popup/display <b>will</b> be hidden. 
091     */
092    public static final EventType<Event> ON_HIDING =
093            new EventType<Event>(Event.ANY, "COMBO_BOX_BASE_ON_HIDING");
094
095    /**
096     * <p>Called when the ComboBox popup/display has been hidden.
097     */
098    public static final EventType<Event> ON_HIDDEN =
099            new EventType<Event>(Event.ANY, "COMBO_BOX_BASE_ON_HIDDEN");
100    
101    
102    
103    /***************************************************************************
104     *                                                                         *
105     * Constructors                                                            *
106     *                                                                         *
107     **************************************************************************/
108    
109    /**
110     * Creates a default ComboBoxBase instance.
111     */
112    public ComboBoxBase() {
113        getStyleClass().add(DEFAULT_STYLE_CLASS);
114    }
115    
116    void valueInvalidated() {
117        fireEvent(new ActionEvent());
118    }
119    
120    /***************************************************************************
121     *                                                                         *
122     * Properties                                                              *
123     *                                                                         *
124     **************************************************************************/  
125    
126    // --- value
127    /**
128     * The value of this ComboBox is defined as the selected item if the input
129     * is not editable, or if it is editable, the most recent user action: 
130     * either the value input by the user, or the last selected item.
131     */
132    public ObjectProperty<T> valueProperty() { return value; }
133    private ObjectProperty<T> value = new SimpleObjectProperty<T>(this, "value") {
134        T oldValue;
135        
136        @Override protected void invalidated() {
137            super.invalidated();
138            T newValue = get();
139            
140            if ((oldValue == null && newValue != null) ||
141                    oldValue != null && ! oldValue.equals(newValue)) {
142                valueInvalidated();
143            }
144            
145            oldValue = newValue;
146        }
147    };
148    public final void setValue(T value) { valueProperty().set(value); }
149    public final T getValue() { return valueProperty().get(); }
150    
151    
152    // --- editable
153    /**
154     * Specifies whether the ComboBox allows for user input. When editable is 
155     * true, the ComboBox has a text input area that a user may type in to. This
156     * input is then available via the {@link #valueProperty() value} property.
157     * 
158     * <p>Note that when the editable property changes, the value property is 
159     * reset, along with any other relevant state.
160     */
161    public BooleanProperty editableProperty() { return editable; }
162    public final void setEditable(boolean value) { editableProperty().set(value); }
163    public final boolean isEditable() { return editableProperty().get(); }
164    private BooleanProperty editable = new SimpleBooleanProperty(this, "editable", false) {
165        @Override protected void invalidated() {
166            pseudoClassStateChanged(PSEUDO_CLASS_EDITABLE, get());
167        }
168    };
169    
170    
171    // --- showing
172    /**
173     * Represents the current state of the ComboBox popup, and whether it is 
174     * currently visible on screen (although it may be hidden behind other windows).
175     */
176    private ReadOnlyBooleanWrapper showing;
177    public ReadOnlyBooleanProperty showingProperty() { return showingPropertyImpl().getReadOnlyProperty(); }
178    public final boolean isShowing() { return showingPropertyImpl().get(); }
179    private void setShowing(boolean value) {
180        // these events will not fire if the showing property is bound
181        Event.fireEvent(this, value ? new Event(ComboBoxBase.ON_SHOWING) :
182            new Event(ComboBoxBase.ON_HIDING));
183        showingPropertyImpl().set(value);
184        Event.fireEvent(this, value ? new Event(ComboBoxBase.ON_SHOWN) : 
185            new Event(ComboBoxBase.ON_HIDDEN));
186    }
187    private ReadOnlyBooleanWrapper showingPropertyImpl() {
188        if (showing == null) {
189            showing = new ReadOnlyBooleanWrapper(false) {
190                @Override protected void invalidated() {
191                    pseudoClassStateChanged(PSEUDO_CLASS_SHOWING, get());
192                }
193
194                @Override
195                public Object getBean() {
196                    return ComboBoxBase.this;
197                }
198
199                @Override
200                public String getName() {
201                    return "showing";
202                }
203            };
204        }
205        return showing;
206    }
207    
208    
209    // --- prompt text
210    /**
211     * The {@code ComboBox} prompt text to display, or <tt>null</tt> if no 
212     * prompt text is displayed. Prompt text is not displayed in all circumstances,
213     * it is dependent upon the subclasses of ComboBoxBase to clarify when
214     * promptText will be shown.
215     */
216    private StringProperty promptText = new SimpleStringProperty(this, "promptText", "") {
217        @Override protected void invalidated() {
218            // Strip out newlines
219            String txt = get();
220            if (txt != null && txt.contains("\n")) {
221                txt = txt.replace("\n", "");
222                set(txt);
223            }
224        }
225    };
226    public final StringProperty promptTextProperty() { return promptText; }
227    public final String getPromptText() { return promptText.get(); }
228    public final void setPromptText(String value) { promptText.set(value); }
229    
230    
231    // --- armed
232    /**
233     * Indicates that the ComboBox has been "armed" such that a mouse release
234     * will cause the ComboBox {@link #show()} method to be invoked. This is 
235     * subtly different from pressed. Pressed indicates that the mouse has been
236     * pressed on a Node and has not yet been released. {@code arm} however
237     * also takes into account whether the mouse is actually over the
238     * ComboBox and pressed.
239     */
240    public BooleanProperty armedProperty() { return armed; }
241    private final void setArmed(boolean value) { armedProperty().set(value); }
242    public final boolean isArmed() { return armedProperty().get(); }
243    private BooleanProperty armed = new SimpleBooleanProperty(this, "armed", false) {
244        @Override protected void invalidated() {
245            pseudoClassStateChanged(PSEUDO_CLASS_ARMED, get());
246        }
247    };
248    
249    
250    // --- On Action
251    /**
252     * The ComboBox action, which is invoked whenever the ComboBox 
253     * {@link #valueProperty() value} property is changed. This
254     * may be due to the value property being programmatically changed, when the
255     * user selects an item in a popup list or dialog, or, in the case of 
256     * {@link #editableProperty() editable} ComboBoxes, it may be when the user 
257     * provides their own input (be that via a {@link TextField} or some other
258     * input mechanism.
259     */
260    public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }
261    public final void setOnAction(EventHandler<ActionEvent> value) { onActionProperty().set(value); }
262    public final EventHandler<ActionEvent> getOnAction() { return onActionProperty().get(); }
263    private ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
264        @Override protected void invalidated() {
265            setEventHandler(ActionEvent.ACTION, get());
266        }
267
268        @Override
269        public Object getBean() {
270            return ComboBoxBase.this;
271        }
272
273        @Override
274        public String getName() {
275            return "onAction";
276        }
277    };
278    
279    
280    // --- On Showing
281    public final ObjectProperty<EventHandler<Event>> onShowingProperty() { return onShowing; }
282    /**
283     * Called just prior to the {@code ComboBoxBase} popup/display being shown, 
284     * @since 2.2
285     */
286    public final void setOnShowing(EventHandler<Event> value) { onShowingProperty().set(value); }
287    public final EventHandler<Event> getOnShowing() { return onShowingProperty().get(); }
288    private ObjectProperty<EventHandler<Event>> onShowing = new ObjectPropertyBase<EventHandler<Event>>() {
289        @Override protected void invalidated() {
290            setEventHandler(ON_SHOWING, get());
291        }
292
293        @Override public Object getBean() {
294            return ComboBoxBase.this;
295        }
296
297        @Override public String getName() {
298            return "onShowing";
299        }
300    };
301
302
303    // -- On Shown
304    public final ObjectProperty<EventHandler<Event>> onShownProperty() { return onShown; }
305    /**
306     * Called just after the {@link ComboBoxBase} popup/display is shown.
307     * @since 2.2
308     */
309    public final void setOnShown(EventHandler<Event> value) { onShownProperty().set(value); }
310    public final EventHandler<Event> getOnShown() { return onShownProperty().get(); }
311    private ObjectProperty<EventHandler<Event>> onShown = new ObjectPropertyBase<EventHandler<Event>>() {
312        @Override protected void invalidated() {
313            setEventHandler(ON_SHOWN, get());
314        }
315
316        @Override public Object getBean() {
317            return ComboBoxBase.this;
318        }
319
320        @Override public String getName() {
321            return "onShown";
322        }
323    };
324
325
326    // --- On Hiding
327    public final ObjectProperty<EventHandler<Event>> onHidingProperty() { return onHiding; }
328    /**
329     * Called just prior to the {@link ComboBox} popup/display being hidden.
330     * @since 2.2
331     */
332    public final void setOnHiding(EventHandler<Event> value) { onHidingProperty().set(value); }
333    public final EventHandler<Event> getOnHiding() { return onHidingProperty().get(); }
334    private ObjectProperty<EventHandler<Event>> onHiding = new ObjectPropertyBase<EventHandler<Event>>() {
335        @Override protected void invalidated() {
336            setEventHandler(ON_HIDING, get());
337        }
338
339        @Override public Object getBean() {
340            return ComboBoxBase.this;
341        }
342
343        @Override public String getName() {
344            return "onHiding";
345        }
346    };
347
348
349    // --- On Hidden
350    public final ObjectProperty<EventHandler<Event>> onHiddenProperty() { return onHidden; }
351    /**
352     * Called just after the {@link ComboBoxBase} popup/display has been hidden.
353     * @since 2.2
354     */
355    public final void setOnHidden(EventHandler<Event> value) { onHiddenProperty().set(value); }
356    public final EventHandler<Event> getOnHidden() { return onHiddenProperty().get(); }
357    private ObjectProperty<EventHandler<Event>> onHidden = new ObjectPropertyBase<EventHandler<Event>>() {
358        @Override protected void invalidated() {
359            setEventHandler(ON_HIDDEN, get());
360        }
361
362        @Override public Object getBean() {
363            return ComboBoxBase.this;
364        }
365
366        @Override public String getName() {
367            return "onHidden";
368        }
369    };
370    
371    
372    /***************************************************************************
373     *                                                                         *
374     * Methods                                                                 *
375     *                                                                         *
376     **************************************************************************/
377
378    /**
379     * Requests that the ComboBox display the popup aspect of the user interface.
380     * As mentioned in the {@link ComboBoxBase} class javadoc, what is actually
381     * shown when this method is called is undefined, but commonly it is some
382     * form of popup or dialog window.
383     */
384    public void show() {
385        if (!isDisabled()) {
386            setShowing(true);
387        }
388    }
389
390    /**
391     * Closes the popup / dialog that was shown when {@link #show()} was called.
392     */
393    public void hide() {
394        if (isShowing()) {
395            setShowing(false);
396        }
397    }
398    
399    /**
400     * Arms the ComboBox. An armed ComboBox will show a popup list on the next 
401     * expected UI gesture.
402     *
403     * @expert This function is intended to be used by experts, primarily
404     *         by those implementing new Skins or Behaviors. It is not common
405     *         for developers or designers to access this function directly.
406     */
407    public void arm() {
408        if (! armedProperty().isBound()) {
409            setArmed(true);
410        }
411    }
412
413    /**
414     * Disarms the ComboBox. See {@link #arm()}.
415     *
416     * @expert This function is intended to be used by experts, primarily
417     *         by those implementing new Skins or Behaviors. It is not common
418     *         for developers or designers to access this function directly.
419     */
420    public void disarm() {
421        if (! armedProperty().isBound()) {
422            setArmed(false);
423        }
424    }
425    
426    /***************************************************************************
427     *                                                                         *
428     * Stylesheet Handling                                                     *
429     *                                                                         *
430     **************************************************************************/
431
432    private static final String DEFAULT_STYLE_CLASS = "combo-box-base";
433    
434    private static final PseudoClass PSEUDO_CLASS_EDITABLE =
435            PseudoClass.getPseudoClass("editable");
436    private static final PseudoClass PSEUDO_CLASS_SHOWING =
437            PseudoClass.getPseudoClass("showing");
438    private static final PseudoClass PSEUDO_CLASS_ARMED =
439            PseudoClass.getPseudoClass("armed");
440    
441}