Way to require TextEditor/Label contents be non-empty?


#1

Hi,

Is there an existing/simple way to require a minimum content length in Labels/TextEditors? For example, if I want someone to be able to edit a preset/program name in a plugin, but I don’t want them to be able to leave a blank name.

Thanks for any help,
Andrew


#2

How could it do that? It has to be possible for someone to clear and re-type the contents, so it can’t prevent you from having too few characters in there! Just check the content yourself when they hit return or whatever.


#3

Thanks for the quick reply. I agree that it has its idiosyncrasies … I just wanted to check to see if anyone had done anything similar before coding the particulars. Many thanks.


#4

Hi,
I’ve done myself a template class using TextEditor to create a generic input field with mandatory and validity checks.
It is relatively simple to make this out.
Best regards.


#5

Hi Nicolas,
Thanks for the thoughts. Have you implemented the simple version of this (i.e. check for empty text after-the-fact and the reset to previous text if empty)? I have that in place, and when I have time I may try to implement the more elegant version (preventing field from ever seeming empty) … it is very stylistic/subjective, of course, but I don’t think the field needs to be allowed to be empty (a user can select all the text and then type the first character of the replacement text in one step … he/she doesn’t need to make an empty text field first and then start typing … I think it can be more confusing to let something look like it is allowed to be empty and then repopulate it with old/unexpected text upon hitting Enter). Thanks again.


#6

Hi there,

Please excuse me for the time I took for answering this thread…
I paste my code just after my post. It is just what I needed and I do not claim having the best or first idea of it.
As you will see, it is just a template class contained in a header file, with a specification for String type because of its special behaviour.
Like every time, it is just a hint and a way of thought !

Best regards,
Nicolas

/*!
 * \file
 * \brief   Definition of a generic input field
 */

#ifndef __GUI_COMPONENT_INPUTFIELD_H__
#define __GUI_COMPONENT_INPUTFIELD_H__

// Includes
#include <juce.h>
#include <boost/lexical_cast.hpp>

// Namespaces
namespace gui {
namespace component {

/*!
 * \class   InputField
 * \brief   Generic input field
 */
template <typename T>
class InputField : public TextEditor,
                   private TextEditorListener
{
public:

    /*!
     * \brief       Default constructor
     * \param[in]   componentName   Name of the component
     * \param[in]   minValue        Minimum value allowed
     * \param[in]   maxValue        Maximum value allowed
     * \param[in]   mandatory       true to set this field as mandatory, false otherwise
     */
    InputField(const String& componentName, T minValue, T maxValue, bool mandatory = false)
        : TextEditor(componentName, 0),
          minValue(minValue),
          maxValue(maxValue),
          mandatory(mandatory)
    {
        // Get default background colour
        dftBgColour = findColour(TextEditor::backgroundColourId);
        // Define background colour according to mandatory state
        setColour(TextEditor::backgroundColourId, mandatory ? Colours::lightpink : dftBgColour);
        // Add this as listener of this
        addListener(this);
    }

    /*!
     * \brief   Default destructor
     */
    ~InputField()
    {
    
    }

    /*!
     * \brief   Check if this field is mandatory
     * \return  true if mandatory, false otherwise
     */
    bool isMandatory()
    {
        return mandatory;
    }

    /*!
     * \brief   Check if input of this field is valid
     * \return  true if valid, false otherwise
     */
    bool isInputValid()
    {
        // Initialize valid state
        bool inputValid = false;
        // If input empty and is not mandatory
        if (isEmpty() && !mandatory)
            inputValid = true;
        // Else if input is not empty
        else if (!isEmpty())
        {
            try
            {
                // Try to cast input into desired object type
                T value = boost::lexical_cast<T>(getText());
                // If success, input is valid
                inputValid = (value >= minValue) && (value <= maxValue);
            }
            catch (boost::bad_lexical_cast&) {}
        }
        // Return valid state
        return inputValid;
    }

    T getValue() const
    {
        // Initialize value
        T value;
        // Try to convert input field into value type
        try
        {
            value = boost::lexical_cast<T>(getText());
        }
        catch (boost::bad_lexical_cast&) {}
        // Return value
        return value;
    }

    //-------------------------------------------------------------------------
    // Overload of TextEditorListener functions
    //-------------------------------------------------------------------------

    /*!
     * \brief       Called when user changes the text
     * \param[in]   editor  Editor which fired this changed signal
     */
    void textEditorTextChanged(TextEditor& editor)
    {
        if (isEmpty() && mandatory)
            setColour(TextEditor::backgroundColourId, Colours::lightpink);
        else if (!isInputValid())
            setColour(TextEditor::backgroundColourId, Colours::red);
        else
            setColour(TextEditor::backgroundColourId, dftBgColour);
    }

private:

    Colour dftBgColour;     // Default background colour
    bool mandatory;         // Mandatory field
    T minValue;             // Allowed minimum value
    T maxValue;             // Allowed maximum value

    // Declare as non copyable, with leak detection
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InputField);
};

/*!
 * \class   InputField
 * \brief   Generic input field
 */
template <>
class InputField<String> : public TextEditor,
                           private TextEditorListener
{
public:

    /*!
     * \brief       Default constructor
     * \param[in]   componentName       Name of the component
     * \param[in]   passwordCharacter   Character used as replacement for drawn characters on screen
     * \param[in]   mandatory           true to set this field as mandatory, false otherwise
     */
    InputField(const String& componentName = String::empty,
               juce_wchar passwordCharacter = 0,
               bool mandatory = false)
        : TextEditor(componentName, passwordCharacter),
          mandatory(mandatory)
    {
        // Get default background colour
        dftBgColour = findColour(TextEditor::backgroundColourId);
        // Define background colour according to mandatory state
        setColour(TextEditor::backgroundColourId, mandatory ? Colours::lightpink : dftBgColour);
        // Add this as listener of this
        addListener(this);
    }

    /*!
     * \brief   Default destructor
     */
    ~InputField()
    {
    
    }

    /*!
     * \brief   Check if this field is mandatory
     * \return  true if mandatory, false otherwise
     */
    bool isMandatory()
    {
        return mandatory;
    }

    /*!
     * \brief   Check if input of this field is valid
     * \return  true if valid, false otherwise
     */
    bool isInputValid()
    {
        return (isEmpty() && mandatory) ? false : true;
    }

    //-------------------------------------------------------------------------
    // Overload of TextEditorListener functions
    //-------------------------------------------------------------------------

    /*!
     * \brief       Called when user changes the text
     * \param[in]   editor  Editor which fired this changed signal
     */
    void textEditorTextChanged(TextEditor& editor)
    {
        setColour(TextEditor::backgroundColourId, isInputValid() ? dftBgColour : Colours::lightpink);
    }

private:

    Colour dftBgColour;     // Default background colour
    bool mandatory;         // Mandatory field

    // Declare as non copyable, with leak detection
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InputField);
};

}   // namespace component
}   // namespace gui

#endif  // __GUI_COMPONENT_INPUTFIELD_H__

#7

Thanks very much for posting your code!