You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/08/07 05:15:38 UTC

[GitHub] jtulach commented on a change in pull request #652: Initial implementation of @ActionState support

jtulach commented on a change in pull request #652: Initial implementation of @ActionState support
URL: https://github.com/apache/incubator-netbeans/pull/652#discussion_r208097274
 
 

 ##########
 File path: openide.awt/src/org/openide/awt/ActionState.java
 ##########
 @@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.openide.awt;
+
+import java.beans.PropertyChangeListener;
+import javax.swing.event.ChangeListener;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.EventListener;
+import javax.swing.Action;
+import org.openide.util.actions.Presenter;
+
+/**
+ * Specifies that the action behaviour is conditional and how the action should obtain the
+ * state for its display. The annotation must be present on the same element as
+ * {@link ActionRegistration} to specify that action is on/off state. The annotation
+ * can be only applied on <b>context actions</b>, which have a single parameter constructor,
+ * which accept the model object - see {@link Actions#context(java.lang.Class, boolean, boolean, org.openide.util.ContextAwareAction, java.lang.String, java.lang.String, java.lang.String, boolean)}.
+ * <p/>
+ * The annotation can be used either as <b>top-level</b>, and will change the action
+ * to <b>toggle on/off action</b>, represented by a checkbox (menu) or a toggle button (toolbar).
+ * The action state will track the model property specified by this
+ * annotation. Toggle actions become <b>enabled</b> when the model object is
+ * found in the Lookup, and <b>checked</b> (or toggled on) when the model property
+ * is set to a defined value (usually {@code true})
+ * <p/>
+ * The {@link #type} specifies type which is searched for in the {@link Lookup} and
+ * if an instance is found, it is used as the model object. If the {@link #type} is not set, 
+ * the <b>the type inferred from Action's
+ * constructor</b> (see {@link ActionRegistration}) will be used to find th model.
+ * <p/>
+ * The {@link #property} specifies bean property whose value should be used to
+ * determine checked state. The obtained value is compared using {@link #checkedValue}
+ * as follows:
+ * <ul>
+ * <li>a boolean or Boolean value is compared to {@link Boolean#TRUE} or the {@link #checkedValue},
+ * if present.
+ * <li>if {@link #checkValue} is {@link #NULL_VALUE}, the action is checked if and only if
+ * the value is {@code null}. 
+ * <li>if {@link #checkValue} is {@link #ANY_VALUE}, the action is checked if and only if
+ * the value is not {@code null}. 
+ * <li>if the value type is an enum, its {@link Enum#name} is compared to {@link #checkValue}
+ * <li>the state will be {@code false} (unchecked) otherwise.
+ * <p/>
+ * If {@link #type} is set to {@link Action}.class, the annotated element <b>must
+ * be an {@link Action}</b> subclass. {@link Action#getValue} will be used to determine
+ * the state. The Action delegate <b>will not be instantiated eagerly</b>, but only
+ * after the necessary context type becomes available in Lookup. 
+ * This support minimizes from premature code loading for custom action implementations. 
+ * <b>Important note:</b> if your Action implements {@link ContextAwareAction},
+ * or one of the {@link Presenter} interfaces, it is eager and will be loaded immediately !
+ * <p/>
+ * Changes to the model object will be tracked using event listener pattern. The annotation-supplied
+ * delegate attempts to {@link PropertyChangeListener} and {@link ChangeListener} automatically; \
+ * other listener interfaces must be specified using {@link #listenOn}
+ * value. Finally, {@link #listenOnMethod} specifies which listener method will trigger
+ * state update; by default, all listener method calls will update the action state.
+ * <p/>
+ * The {@link ActionState} annotation may be also used as a value of {@link ActionRegistration#enabledOn()} 
+ * and causes the annotated Action to be <b>enabled</b> not only on presence of object of the context type,
+ * but also based on the model property. The property, enable value and listener is specified the
+ * same way as for "checked" state. See the above text.
+ * <p/>
+ * If a completely custom behaviour is desired, the system can finally delegate {@link Action#isEnabled} and
+ * {@link Action#getValue({@link Action#SELECTED_KEY}) to the action implementation itself: use {@link #useActionInstance()}
+ * value.
+ * <p/>
+ * Here are several examples of {@code @ActionState} usage:
+ * <p/>
+ * To define action, which <b>enables on modified DataObjects</b> do the following
+ * registration:
+ * <code><pre>
+ * &#64;ActionID(category = "Example", id = "example.SaveAction")
+ * &#64;ActionRegistration(displayName = "Save modified",
+ *     enabledOn = @ActionState(property = "modified")
+ * )
+ * public class ExampleAction implements ActionListener {
+ *     public ExampleAction(DataObject d) {
+ *         // ...
+ *     }
+ *     
+ *     public void actionPerformed(ActionEvent e) {
+ *         // ...
+ *     }
+ * }
+ * </pre></code>
+ * The action will be instantiated and run only after:
+ * <ul>
+ * <li>DataObject becomes available, and
+ * <li>its {@code modified} property becomes true
+ * </ul>
+ * 
+ * To create "toggle" action in toolbar or a menu, which changes state based on some property,
+ * you can code:
+ * <code><pre>
+ * enum SelectionMode {
+ *     Rectangular,
+ *     normal
+ * }
+ * &#64;ActionID(category = "Example", id = "example.RectSelection")
+ * &#64;ActionRegistration(displayName = "Toggle rectangular selection")
+ * &#64;ActionState(property = "selectionMode", checkedValue = "Rectangular", listenOn = EditorStateListener.class)
+ * public class RectangularSelectionAction implements ActionListener {
+ *     public RectangularSelectionAction(EditorInterface editor) {
+ *         // ...
+ *     }
+ *     &#64;Override
+ *     public void actionPerformed(ActionEvent e) {
+ *     }
+ * }
+ * </pre></code>
+ * The action enables when {@code EditorInterface} appears in the action Lookup. Then,
+ * its state will be derived from {@code EditorInterface.selectionMode} property. Since
+ * there's a custom listener interface for this value, it must be specified using {@link #listenOn}.
+ * <p/>
+ * Finally, if the action needs to perform its own special magic to check enable state, we 
+ * hand over final control to the action, but the annotation-introduced wrappers will still
+ * create action instance for a new model object, attach and detach listeners on it and ensure
+ * that UI will not be strongly referenced from the model for proper garbage collection:
+ * <code><pre>
+ * &#64;ActionID(category = "Example", id = "example.SelectPrevious")
+ * &#64;ActionRegistration(displayName = "Selects previous item")
+ * &#64;ActionState(listenOn = ListSelectionListener.class, useActionInstance = true)
+ * public class SelectPreviousAction extends AbstractAction {
+ *     private final ListSelectionModel model;
+ *     
+ *     public SelectPreviousAction(ListSelectionModel model) {
+ *         this.model = model;
+ *     }
+ *     &#64;Override
+ *     public boolean isEnabled() {
+ *         return model.getAnchorSelectionIndex() > 0;
+ *     }
+ *     &#64;Override
+ *     public void actionPerformed(ActionEvent e) {
+ *     }
+ * }
+ * </pre></code>
+ * The system will do the necessary bookkeeping, but the action decides using its
+ * {@link Action#isEnabled} implementation. 
+ * 
+ * @author sdedic
+ * @since 7.71
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+public @interface ActionState {
+
+    /**
+     * The type which the action will look for in its context. The action
+     * becomes checked (enabled) if and only if there's at least one instance of such type
+     * present in the context. There are some special values that modify behaviour:
+     * <ul>
+     * <li><code>Object.class</code> (the default) the context object will be used and the property will be read
+     * from the context object. Only applicable if the action accepts single object.
+     * <li><code>Action.class</code>: the {@link #property} action value will be used, 
+     * as obtained by {@link Action#getValue}.
+     * </ul>
+     * If {@code @ActionState} is used in {@link ActionRegistration#enabledOn()}, the
+     * {@code type} can be left blank, defaulting to the context type for the action.
+     *
+     * @return type to work with.
+     */
+    public Class<?> type() default Object.class;
+
+    /**
+     * Property name whose value represents the state. The property must be a
+     * property on the {@link #type()} class; read-only properties are
+     * supported. If the target class as supports attaching
 
 Review comment:
   ...as supports!?

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists