Search in sources :

Example 21 with UI

use of com.codename1.rad.ui.UI in project CodenameOne by codenameone.

the class ResetableTextWatcher method edit.

/**
 * Entry point for using this class
 * @param impl The current running activity
 * @param component Any subclass of com.codename1.ui.TextArea
 * @param inputType One of the TextArea's input-type constants
 */
public static void edit(final AndroidImplementation impl, final Component component, final int inputType) {
    if (impl.getActivity() == null) {
        throw new IllegalArgumentException("activity is null");
    }
    if (component == null) {
        throw new IllegalArgumentException("component is null");
    }
    if (!(component instanceof TextArea)) {
        throw new IllegalArgumentException("component must be instance of TextArea");
    }
    final TextArea textArea = (TextArea) component;
    textArea.registerAsInputDevice();
    final String initialText = textArea.getText();
    textArea.putClientProperty("InPlaceEditView.initialText", initialText);
    // The very first time we try to edit a string, let's determine if the
    // system default is to do async editing.  If the system default
    // is not yet set, we set it here, and it will be used as the default from now on
    // We do this because the nativeInstance.isAsyncEditMode() value changes
    // to reflect the currently edited field so it isn't a good way to keep a
    // system default.
    String defaultAsyncEditingSetting = Display.getInstance().getProperty("android.VKBAlwaysOpen", null);
    if (defaultAsyncEditingSetting == null) {
        defaultAsyncEditingSetting = impl.isAsyncEditMode() ? "true" : "false";
        Display.getInstance().setProperty("android.VKBAlwaysOpen", defaultAsyncEditingSetting);
    }
    boolean asyncEdit = "true".equals(defaultAsyncEditingSetting) ? true : false;
    // Check if the form has any setting for asyncEditing that should override
    // the application defaults.
    final Form parentForm = component.getComponentForm();
    if (parentForm == null) {
        com.codename1.io.Log.p("Attempt to edit text area that is not on a form.  This is not supported");
        return;
    }
    if (parentForm.getClientProperty("asyncEditing") != null) {
        Object async = parentForm.getClientProperty("asyncEditing");
        if (async instanceof Boolean) {
            asyncEdit = ((Boolean) async).booleanValue();
        // Log.p("Form overriding asyncEdit due to asyncEditing client property: "+asyncEdit);
        }
    }
    if (parentForm.getClientProperty("android.asyncEditing") != null) {
        Object async = parentForm.getClientProperty("android.asyncEditing");
        if (async instanceof Boolean) {
            asyncEdit = ((Boolean) async).booleanValue();
        // Log.p("Form overriding asyncEdit due to ios.asyncEditing client property: "+asyncEdit);
        }
    }
    if (parentForm.isFormBottomPaddingEditingMode()) {
        asyncEdit = true;
    }
    // then this will override all other settings.
    if (component.getClientProperty("asyncEditing") != null) {
        Object async = component.getClientProperty("asyncEditing");
        if (async instanceof Boolean) {
            asyncEdit = ((Boolean) async).booleanValue();
        // Log.p("Overriding asyncEdit due to field asyncEditing client property: "+asyncEdit);
        }
    }
    if (component.getClientProperty("android.asyncEditing") != null) {
        Object async = component.getClientProperty("android.asyncEditing");
        if (async instanceof Boolean) {
            asyncEdit = ((Boolean) async).booleanValue();
        // Log.p("Overriding asyncEdit due to field ios.asyncEditing client property: "+asyncEdit);
        }
    }
    final boolean resizeEditMode = "resize".equalsIgnoreCase(String.valueOf(component.getClientProperty("android.editMode")));
    final boolean panEditMode = "pan".equalsIgnoreCase(String.valueOf(component.getClientProperty("android.editMode")));
    // if true, then in async mode we are currently editing and are switching to another field
    final boolean isEditedFieldSwitch;
    // If we are already editing, we need to finish that up before we proceed to edit the next field.
    synchronized (editingLock) {
        if (mIsEditing) {
            if (impl.isAsyncEditMode()) {
                // Using isEditedFieldSwitch was causing issues with cursors not showing up.
                // https://github.com/codenameone/CodenameOne/issues/2353
                // https://stackoverflow.com/questions/49004370/focus-behaviour-in-textarea-in-cn1
                // Disabling this feature by default now, but can be re-enabled by setting
                // Display.getInstance().setProperty("android.reuseTextEditorOnSwitch", "true");
                // This editedFieldSwitch feature was added a while back to improve experience on older
                // Android devices where the field switching was going too slow.
                // https://github.com/codenameone/CodenameOne/issues/2012
                // This issue was resolved in this commit (https://github.com/jaanushansen/CodenameOne/commit/f3e53a80704149e4d7cde276d01c1368bcdcfe2c)
                // which was submitted as part of a pull request.  This fix has been the source of several
                // regressions, mostly related to properties not being propagated properly when a text field is changed
                // However, this issue (with the cursor not showing up), doesn't appear to have a simple solution
                // so, I'm disabling this feature for now.
                isEditedFieldSwitch = "true".equals(Display.getInstance().getProperty("android.reuseTextEditorOnSwitch", "false"));
                final String[] out = new String[1];
                TextArea prevTextArea = null;
                if (sInstance != null && sInstance.mLastEditText != null) {
                    prevTextArea = sInstance.mLastEditText.getTextArea();
                }
                if (prevTextArea != null) {
                    final TextArea fPrevTextArea = prevTextArea;
                    final String retVal = sInstance.mLastEditText.getText().toString();
                    Display.getInstance().callSerially(new Runnable() {

                        public void run() {
                            Display.getInstance().onEditingComplete(fPrevTextArea, retVal);
                            textArea.requestFocus();
                        }
                    });
                }
                InPlaceEditView.setEditedTextField(textArea);
                nextTextArea = null;
            } else {
                isEditedFieldSwitch = false;
                final InPlaceEditView instance = sInstance;
                if (instance != null && instance.mEditText != null && instance.mEditText.mTextArea == textArea) {
                    instance.showTextEditorAgain();
                    return;
                }
                if (!isClosing && sInstance != null && sInstance.mEditText != null) {
                    isClosing = true;
                    impl.getActivity().runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            instance.endEditing(REASON_UNDEFINED, true, 0);
                        }
                    });
                }
                afterClose = new Runnable() {

                    @Override
                    public void run() {
                        impl.callHideTextEditor();
                        Display.getInstance().editString(component, textArea.getMaxSize(), inputType, textArea.getText());
                    }
                };
                return;
            }
        } else {
            isEditedFieldSwitch = false;
        }
        mIsEditing = true;
        isClosing = false;
        afterClose = null;
    }
    impl.setAsyncEditMode(asyncEdit);
    // textArea.setPreferredSize(prefSize);
    if (!impl.isAsyncEditMode() && textArea instanceof TextField) {
        ((TextField) textArea).setEditable(false);
    }
    final boolean scrollableParent = isScrollableParent(textArea);
    // We wrap the text area so that we can safely pass data across to the
    // android UI thread.
    final TextAreaData textAreaData = new TextAreaData(textArea);
    impl.getActivity().runOnUiThread(new Runnable() {

        @Override
        public void run() {
            if (!isEditedFieldSwitch) {
                releaseEdit();
                if (sInstance == null) {
                    sInstance = new InPlaceEditView(impl);
                    impl.relativeLayout.addView(sInstance);
                }
            // Let's try something new here
            // We'll ALWAYS try resize edit mode (since it just works better)
            // But we'll detect whether the field is still covered by the keyboard
            // and switch to pan mode if necessary.
            }
            if (panEditMode) {
                setEditMode(false);
            } else if (resizeEditMode) {
                setEditMode(true);
            } else if (parentForm.isFormBottomPaddingEditingMode()) {
                setEditMode(true);
            } else if (scrollableParent) {
                setEditMode(false);
            } else {
                trySetEditMode(true);
            }
            sInstance.startEditing(impl.getActivity(), textAreaData, initialText, inputType, isEditedFieldSwitch);
        }
    });
    final String[] out = new String[1];
    // In case the contents of the text area are changed while editing is in progress
    // we should propagate the changes to the native text field.
    final DataChangedListener textAreaDataChanged = new DataChangedListener() {

        @Override
        public void dataChanged(int type, int index) {
            if (suppressDataChangedEvent) {
                // https://github.com/codenameone/CodenameOne/issues/3343
                return;
            }
            TextArea currTextArea = getCurrentTextArea();
            if (currTextArea != textArea) {
                // This is not the active text area anymore
                textArea.removeDataChangedListener(this);
                return;
            }
            final String newText = textArea.getText();
            EditView currEditView = getCurrentEditView();
            if (currEditView == null || currEditView.mTextArea != textArea) {
                textArea.removeDataChangedListener(this);
                return;
            }
            String existingText = currEditView.getText().toString();
            // because Objects.equals was not available until API 19
            if (!com.codename1.compat.java.util.Objects.equals(newText, existingText)) {
                impl.getActivity().runOnUiThread(new Runnable() {

                    public void run() {
                        TextArea currTextArea = getCurrentTextArea();
                        EditView currEditView = getCurrentEditView();
                        if (currTextArea != textArea || currEditView == null || currEditView.mTextArea != textArea) {
                            return;
                        }
                        String existingText = currEditView.getText().toString();
                        // because Objects.equals was not available until API 19
                        if (!com.codename1.compat.java.util.Objects.equals(newText, existingText)) {
                            // We need to suppress the Android text change events
                            // to prevent weird things from happening.  E.g. https://github.com/codenameone/CodenameOne/issues/3349
                            suppressTextChangeEvent = true;
                            currEditView.setText(newText);
                            suppressTextChangeEvent = false;
                        }
                    }
                });
            }
        }
    };
    textArea.addDataChangedListener(textAreaDataChanged);
    // In order to reuse the code the runs after edit completion, we will wrap it in a runnable
    // For sync edit mode, we will just run onComplete.run() at the end of this method.  For
    // Async mode we add the Runnable to the textarea as a client property, then run it
    // when editing eventually completes.
    Runnable onComplete = new Runnable() {

        public void run() {
            textArea.removeDataChangedListener(textAreaDataChanged);
            if (!impl.isAsyncEditMode() && textArea instanceof TextField) {
                ((TextField) textArea).setEditable(true);
            }
            textArea.setPreferredSize(null);
            if (sInstance != null && sInstance.mLastEditText != null && sInstance.mLastEditText.mTextArea == textArea) {
                String retVal = sInstance.mLastEditText.getText().toString();
                if (!impl.isAsyncEditMode()) {
                    sInstance.mLastEditText = null;
                    impl.getActivity().runOnUiThread(new Runnable() {

                        public void run() {
                            releaseEdit();
                        }
                    });
                }
                out[0] = retVal;
            } else {
                out[0] = initialText;
            }
            Display.getInstance().onEditingComplete(component, out[0]);
            if (impl.isAsyncEditMode()) {
                impl.callHideTextEditor();
            } else {
                // lock.
                if (sInstance != null) {
                    Display.getInstance().invokeAndBlock(new Runnable() {

                        public void run() {
                            while (sInstance != null) {
                                com.codename1.io.Util.sleep(5);
                            }
                        }
                    });
                }
            }
            // Release the editing flag
            synchronized (editingLock) {
                mIsEditing = false;
            }
            // as a runnable ... this should take priority over the "nextTextArea" setting
            if (afterClose != null) {
                Display.getInstance().callSerially(afterClose);
            } else if (nextTextArea != null) {
                final TextArea next = nextTextArea;
                nextTextArea = null;
                next.requestFocus();
                Display.getInstance().callSerially(new Runnable() {

                    public void run() {
                        Display.getInstance().editString(next, next.getMaxSize(), next.getConstraint(), next.getText());
                    }
                });
            }
        }
    };
    textArea.requestFocus();
    textArea.repaint();
    if (impl.isAsyncEditMode()) {
        component.putClientProperty("android.onAsyncEditingComplete", onComplete);
        return;
    }
    // Make this call synchronous
    // We set this flag so that waitForEditCompletion can block on it.
    // The flag will be released inside the endEditing method which will
    // allow the method to proceed.
    waitingForSynchronousEditingCompletion = true;
    waitForEditCompletion();
    onComplete.run();
}
Also used : TextArea(com.codename1.ui.TextArea) Form(com.codename1.ui.Form) DataChangedListener(com.codename1.ui.events.DataChangedListener) TextField(com.codename1.ui.TextField)

Example 22 with UI

use of com.codename1.rad.ui.UI in project CodenameOne by codenameone.

the class CloudObject method bindTree.

/**
 * Binds a UI tree to the cloud object so its values automatically update in the cloud object
 *
 * @param ui the component tree to bind
 * @param defer bind settings whether to defer the binding which requires developers to explicitly commit
 * the binding to perform the changes
 * @param objectLead if set to true the UI property is initialized from values in the CloudObject, if false
 * the cloud object property is initialized from the UI
 */
public void bindTree(Container ui, int defer, boolean objectLead) {
    int componentCount = ui.getComponentCount();
    for (int iter = 0; iter < componentCount; iter++) {
        Component c = ui.getComponentAt(iter);
        if (c instanceof Container) {
            bindTree((Container) c, defer, objectLead);
            continue;
        }
        String bind = c.getCloudBoundProperty();
        if (bind != null && bind.length() > 0) {
            String attributeName = c.getCloudDestinationProperty();
            if (attributeName != null) {
                bindProperty(c, bind, attributeName, defer, objectLead);
            }
        }
    }
}
Also used : Container(com.codename1.ui.Container) Component(com.codename1.ui.Component)

Example 23 with UI

use of com.codename1.rad.ui.UI in project CodenameOne by codenameone.

the class CloudObject method bindProperty.

/**
 * Binds a property value within the given component to this cloud object, this means that
 * when the component changes the cloud object changes unless deferred. If the defer flag is
 * false all changes are stored in a temporary location and only "committed" once commitBindings()
 * is invoked.
 * @param cmp the component to bind
 * @param propertyName the name of the property in the bound component
 * @param attributeName the key within the cloud object
 * @param defer bind settings whether to defer the binding which requires developers to explicitly commit
 * the binding to perform the changes
 * @param objectLead if set to true the UI property is initialized from values in the CloudObject, if false
 * the cloud object property is initialized from the UI
 */
public void bindProperty(Component cmp, final String propertyName, final String attributeName, final int defer, boolean objectLead) {
    if (objectLead) {
        Object val = values.get(attributeName);
        Object cmpVal = cmp.getBoundPropertyValue(propertyName);
        if (val == null) {
            if (cmpVal != null) {
                cmp.setBoundPropertyValue(propertyName, null);
            }
        } else {
            if (cmpVal == null || !(val.equals(cmpVal))) {
                cmp.setBoundPropertyValue(propertyName, val);
            }
        }
    } else {
        Object val = values.get(attributeName);
        Object cmpVal = cmp.getBoundPropertyValue(propertyName);
        if (cmpVal == null) {
            if (val != null) {
                values.remove(attributeName);
                status = STATUS_MODIFIED;
            }
        } else {
            if (val == null || !(val.equals(cmpVal))) {
                values.put(attributeName, cmpVal);
                status = STATUS_MODIFIED;
            }
        }
    }
    BindTarget target = new BindTarget() {

        public void propertyChanged(Component source, String propertyName, Object oldValue, Object newValue) {
            switch(defer) {
                case BINDING_DEFERRED:
                    if (deferedValues == null) {
                        deferedValues = new Hashtable();
                    }
                    deferedValues.put(attributeName, newValue);
                    break;
                case BINDING_IMMEDIATE:
                    values.put(attributeName, newValue);
                    status = STATUS_MODIFIED;
                    break;
                case BINDING_AUTO_SAVE:
                    values.put(attributeName, newValue);
                    status = STATUS_MODIFIED;
                    CloudStorage.getInstance().save(CloudObject.this);
                    break;
            }
        }
    };
    cmp.bindProperty(propertyName, target);
    cmp.putClientProperty("CN1Bind" + propertyName, target);
}
Also used : Hashtable(java.util.Hashtable) BindTarget(com.codename1.cloud.BindTarget) Component(com.codename1.ui.Component)

Example 24 with UI

use of com.codename1.rad.ui.UI in project CodenameOne by codenameone.

the class ClearableTextField method wrap.

/**
 * Wraps the given text field with a UI that will allow us to clear it
 * @param tf the text field
 * @param iconSize size in millimeters for the clear icon, -1 for default size
 * @return a Container that should be added to the UI instead of the actual text field
 */
public static ClearableTextField wrap(final TextArea tf, float iconSize) {
    ClearableTextField cf = new ClearableTextField();
    Button b = new Button("", tf.getUIID());
    if (iconSize > 0) {
        FontImage.setMaterialIcon(b, FontImage.MATERIAL_CLEAR, iconSize);
    } else {
        FontImage.setMaterialIcon(b, FontImage.MATERIAL_CLEAR);
    }
    removeCmpBackground(tf);
    removeCmpBackground(b);
    cf.setUIID(tf.getUIID());
    cf.add(BorderLayout.CENTER, tf);
    cf.add(BorderLayout.EAST, b);
    b.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent evt) {
            tf.stopEditing();
            tf.setText("");
            tf.startEditingAsync();
        }
    });
    return cf;
}
Also used : ActionListener(com.codename1.ui.events.ActionListener) Button(com.codename1.ui.Button) ActionEvent(com.codename1.ui.events.ActionEvent)

Example 25 with UI

use of com.codename1.rad.ui.UI in project CodenameOne by codenameone.

the class InstantUI method createEntryForProperty.

private void createEntryForProperty(PropertyBase b, Container cnt, ArrayList<UiBinding.Binding> allBindings, UiBinding uib) throws RuntimeException {
    if (isExcludedProperty(b)) {
        return;
    }
    Class cls = (Class) b.getClientProperty("cn1$cmpCls");
    if (cls != null) {
        try {
            Component cmp = (Component) cls.newInstance();
            cmp.setName(b.getName());
            cnt.add(b.getLabel()).add(cmp);
            allBindings.add(uib.bind(b, cmp));
        } catch (Exception err) {
            Log.e(err);
            throw new RuntimeException("Custom property instant UI failed for " + b.getName() + " " + err);
        }
        return;
    }
    String[] multiLabels = (String[]) b.getClientProperty("cn1$multiChceLbl");
    if (multiLabels != null) {
        // multi choice component
        final Object[] multiValues = (Object[]) b.getClientProperty("cn1$multiChceVal");
        if (multiLabels.length < 5) {
            // toggle buttons
            ButtonGroup bg = new ButtonGroup();
            RadioButton[] rbs = new RadioButton[multiLabels.length];
            cnt.add(b.getLabel());
            Container radioBox = new Container(new GridLayout(multiLabels.length));
            for (int iter = 0; iter < multiLabels.length; iter++) {
                rbs[iter] = RadioButton.createToggle(multiLabels[iter], bg);
                radioBox.add(rbs[iter]);
            }
            cnt.add(radioBox);
            allBindings.add(uib.bindGroup(b, multiValues, rbs));
        } else {
            Picker stringPicker = new Picker();
            stringPicker.setStrings(multiLabels);
            Map<Object, Object> m1 = new HashMap<Object, Object>();
            Map<Object, Object> m2 = new HashMap<Object, Object>();
            for (int iter = 0; iter < multiLabels.length; iter++) {
                m1.put(multiLabels[iter], multiValues[iter]);
                m2.put(multiValues[iter], multiLabels[iter]);
            }
            cnt.add(b.getLabel()).add(stringPicker);
            allBindings.add(uib.bind(b, stringPicker, new UiBinding.PickerAdapter<Object>(new UiBinding.MappingConverter(m1), new UiBinding.MappingConverter(m2))));
        }
        return;
    }
    Class t = b.getGenericType();
    if (t != null) {
        if (t == Boolean.class) {
            CheckBox cb = new CheckBox();
            uib.bind(b, cb);
            cnt.add(b.getLabel()).add(cb);
            return;
        }
        if (t == Date.class) {
            Picker dp = new Picker();
            dp.setType(Display.PICKER_TYPE_DATE);
            uib.bind(b, dp);
            cnt.add(b.getLabel()).add(dp);
            return;
        }
    }
    TextField tf = new TextField();
    tf.setConstraint(getTextFieldConstraint(b));
    uib.bind(b, tf);
    cnt.add(b.getLabel()).add(tf);
}
Also used : HashMap(java.util.HashMap) RadioButton(com.codename1.ui.RadioButton) Container(com.codename1.ui.Container) GridLayout(com.codename1.ui.layouts.GridLayout) ButtonGroup(com.codename1.ui.ButtonGroup) CheckBox(com.codename1.ui.CheckBox) Picker(com.codename1.ui.spinner.Picker) TextField(com.codename1.ui.TextField) Component(com.codename1.ui.Component)

Aggregations

Component (com.codename1.ui.Component)10 Label (com.codename1.ui.Label)9 ArrayList (java.util.ArrayList)9 Container (com.codename1.ui.Container)8 Form (com.codename1.ui.Form)8 IOException (java.io.IOException)8 TextArea (com.codename1.ui.TextArea)6 BorderLayout (com.codename1.ui.layouts.BorderLayout)6 HashMap (java.util.HashMap)6 Vector (java.util.Vector)6 Entity (com.codename1.rad.models.Entity)5 Hashtable (java.util.Hashtable)5 EntityList (com.codename1.rad.models.EntityList)4 ActionNode (com.codename1.rad.nodes.ActionNode)4 EncodedImage (com.codename1.ui.EncodedImage)4 FontImage (com.codename1.ui.FontImage)4 Border (com.codename1.ui.plaf.Border)4 Log (com.codename1.io.Log)3 EntityTypeBuilder.entityTypeBuilder (com.codename1.rad.models.EntityTypeBuilder.entityTypeBuilder)3 Thing (com.codename1.rad.schemas.Thing)3