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.
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.
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.
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.
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.
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__