Implemented osgUI::Validator, IntValidator and DoubleValidator classes that manage validation/specialization of LineEdit widgets to work with just integer or double values.

git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14398 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
Robert Osfield 2014-08-05 18:32:45 +00:00
parent 0335e9c63a
commit fdd9efe45a
10 changed files with 491 additions and 35 deletions

View File

@ -123,6 +123,14 @@ inline CallbackObject* getCallbackObject(osg::Object* object, const std::string&
return udc ? dynamic_cast<osg::CallbackObject*>(udc->getUserObject(name)) : 0;
}
/** Convinience function for getting the CallbackObject associated with specificed name from an Object's UserDataContainer.*/
inline const CallbackObject* getCallbackObject(const osg::Object* object, const std::string& name)
{
const osg::UserDataContainer* udc = object->getUserDataContainer();
return udc ? dynamic_cast<const osg::CallbackObject*>(udc->getUserObject(name)) : 0;
}
/** Call run(..) on named CallbackObjects attached to specified Object. Return true if at least one CallbackObject has been successfully invoked.*/
inline bool runNamedCallbackObjects(osg::Object* object, const std::string& name, osg::Parameters& inputParameters, osg::Parameters& outputParameters)
{

View File

@ -15,6 +15,7 @@
#define OSGUI_LINEEDIT
#include <osgUI/Widget>
#include <osgUI/Validator>
#include <osgText/Text>
namespace osgUI
@ -35,9 +36,9 @@ public:
virtual void enterImplementation();
virtual void leaveImplementation();
virtual bool validate(const std::string& text);
virtual bool validateImplementation(const std::string& text);
void setValidator(Validator* validator) { _validator = validator; }
Validator* getValidator() { return _validator.get(); }
const Validator* getValidator() const { return _validator.get(); }
virtual void textChanged(const std::string& text);
virtual void textChangedImplementation(const std::string& text);
@ -48,6 +49,8 @@ public:
protected:
virtual ~LineEdit() {}
osg::ref_ptr<Validator> _validator;
std::string _text;
// implementation detail

118
include/osgUI/Validator Normal file
View File

@ -0,0 +1,118 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGUI_VALIDATOR
#define OSGUI_VALIDATOR
#include <osg/Object>
#include <osgUI/Export>
namespace osgUI
{
class OSGUI_EXPORT Validator : public osg::Object
{
public:
Validator();
Validator(const Validator& validator, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
META_Object(osgUI, Validator);
enum State
{
INVALID,
INTERMEDIATE,
ACCEPTABLE
};
/** entry point to validate(..) method, checks for "validate" CallbackObject and calls it if present, otherwise calls validateImplementation(..)
str parameter is the string that needs to be validated
cursorpos is the position of the cursor within the str string.
return validatidy State. */
virtual State validate(std::string& /*str*/, int& cursorpos) const;
/// override in subclasses to proviude the validate implementation.
virtual State validateImplementation(std::string& /*str*/, int& /*cursorpos*/) const;
/** entry point to fixup, checks for "validate" Callbac Object and calls it if present, otherwise calls validateImplementation(..)
fixup(..) is called when user pressers return/enter in a field being edited.
str parameter is string that needs to be corrected.*/
virtual void fixup(std::string& /*str*/) const;
/// override in subclass to provide the fixup implementation.
virtual void fixupImplementation(std::string& /*str*/) const;
protected:
virtual ~Validator() {}
};
class OSGUI_EXPORT IntValidator : public Validator
{
public:
IntValidator();
IntValidator(const IntValidator& widget, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
META_Object(osgUI, IntValidator);
/// set the bottom value that is accepted as valid, default -INT_MAX
void setBottom(int bottom) { _bottom = bottom; }
int getBottom() const { return _bottom; }
/// set the top value that is accepted as valid, default INT_MAX
void setTop(int top) { _top = top; }
int getTop() const { return _top; }
/// override validate implementation.
virtual State validateImplementation(std::string& str, int& cursorpos) const;
/// override validate implementation.
virtual void fixupImplementation(std::string& str) const;
protected:
virtual ~IntValidator() {}
int _bottom;
int _top;
};
class OSGUI_EXPORT DoubleValidator : public Validator
{
public:
DoubleValidator();
DoubleValidator(const DoubleValidator& widget, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
META_Object(osgUI, DoubleValidator);
/** set the number of decimal places to accept, default is -1,
all negative values disable validation against maximum number of places thus allows any number of decimals places. */
void setDecimals(int numDecimals) { _decimals = numDecimals; }
int getDecimals() const { return _decimals; }
/// set the bottom value that is accepted as valid, default -DBL_MAX
void setBottom(double bottom) { _bottom = bottom; }
double getBottom() const { return _bottom; }
/// set the top value that is accepted as valid, default DBL_MAX
void setTop(double top) { _top = top; }
double getTop() const { return _top; }
/// override validate implementation.
virtual State validateImplementation(std::string& str, int& cursorpos) const;
/// override validate implementation.
virtual void fixupImplementation(std::string& str) const;
protected:
virtual ~DoubleValidator() {}
int _decimals;
double _bottom;
double _top;
};
}
#endif

View File

@ -21,6 +21,7 @@ SET(TARGET_H
${HEADER_PATH}/AlignmentSettings
${HEADER_PATH}/FrameSettings
${HEADER_PATH}/TextSettings
${HEADER_PATH}/Validator
)
SET(TARGET_SRC
@ -36,6 +37,7 @@ SET(TARGET_SRC
AlignmentSettings.cpp
FrameSettings.cpp
TextSettings.cpp
Validator.cpp
${OPENSCENEGRAPH_VERSIONINFO_RC}
)

View File

@ -58,6 +58,17 @@ bool LineEdit::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event
}
else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Return )
{
if (_validator.valid())
{
std::string text_copy(_text);
int cursorpos;
if (_validator->validate(text_copy, cursorpos)==Validator::INTERMEDIATE)
{
_validator->fixup(text_copy);
}
if (text_copy!=_text) setText(text_copy);
}
returnPressed();
return true;
}
@ -74,10 +85,17 @@ bool LineEdit::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event
void LineEdit::setText(const std::string& text)
{
if (!validate(text)) return;
if (_text==text) return;
_text = text;
std::string text_copy(text);
if (_validator.valid())
{
int cursorpos = 0;
Validator::State state = _validator->validate(text_copy, cursorpos);
if (state==Validator::INVALID) return;
}
_text = text_copy;
textChanged(_text);
@ -97,36 +115,6 @@ void LineEdit::leaveImplementation()
if (_backgroundSwitch.valid()) _backgroundSwitch->setSingleChildOn(0);
}
bool LineEdit::validate(const std::string& text)
{
osg::CallbackObject* co = getCallbackObject(this, "validate");
if (co)
{
osg::Parameters inputParameters, outputParameters;
inputParameters.push_back(new osg::StringValueObject("text",text));
if (co->run(this, inputParameters, outputParameters))
{
if (outputParameters.size()>=1)
{
osg::Object* object = outputParameters[0].get();
osg::BoolValueObject* bvo = dynamic_cast<osg::BoolValueObject*>(object);
if (bvo)
{
OSG_NOTICE<<"Have bool return value from validate "<<bvo->getValue()<<std::endl;
return bvo->getValue();
}
OSG_NOTICE<<"Called validate CallbackObject but didn't get bool return value."<<object->className()<<std::endl;
}
}
}
return validateImplementation(text);
}
bool LineEdit::validateImplementation(const std::string& text)
{
OSG_NOTICE<<"LineEdit::validateImplemetation("<<text<<")"<<std::endl;
return true;
}
void LineEdit::textChanged(const std::string& text)
{

290
src/osgUI/Validator.cpp Normal file
View File

@ -0,0 +1,290 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <osgUI/Validator>
#include <osg/ValueObject>
#include <osg/Callback>
#include <sstream>
#include <limits.h>
using namespace osgUI;
Validator::Validator()
{
}
Validator::Validator(const osgUI::Validator& validator, const osg::CopyOp& copyop):
osg::Object(validator, copyop)
{
}
Validator::State Validator::validate(std::string& text, int& cursorpos) const
{
const osg::CallbackObject* co = getCallbackObject(this, "validate");
if (co)
{
osg::ref_ptr<osg::StringValueObject> textInput = new osg::StringValueObject("text",text);
osg::ref_ptr<osg::IntValueObject> cursorposInput = new osg::IntValueObject("cursorpos",cursorpos);
osg::Parameters inputParameters, outputParameters;
inputParameters.push_back(textInput.get());
inputParameters.push_back(cursorposInput.get());
if (co->run(const_cast<Validator*>(this), inputParameters, outputParameters))
{
if (textInput->getValue()!=text)
{
OSG_NOTICE<<"Updating text in CallbackObject "<<textInput->getValue()<<std::endl;
text = textInput->getValue();
}
if (cursorposInput->getValue()!=cursorpos)
{
OSG_NOTICE<<"Updating cursor pos in CallbackObject "<<cursorposInput->getValue()<<std::endl;
cursorpos = cursorposInput->getValue();
}
if (outputParameters.size()>=1)
{
osg::Object* object = outputParameters[0].get();
osg::StringValueObject* svo = dynamic_cast<osg::StringValueObject*>(object);
if (svo)
{
OSG_NOTICE<<"Have string return value from validate "<<svo->getValue()<<std::endl;
std::string returnString = svo->getValue();
if (returnString=="INVALID") return INVALID;
else if (returnString=="INTERMEDITATE") return INTERMEDIATE;
else if (returnString=="ACCEPTABLE") return ACCEPTABLE;
}
OSG_NOTICE<<"Called validate CallbackObject but didn't get string return value."<<object->className()<<std::endl;
}
}
}
return validateImplementation(text, cursorpos);
}
Validator::State Validator::validateImplementation(std::string& text, int& cursorpos) const
{
OSG_NOTICE<<"Validator::validateImplemetation("<<text<<", "<<cursorpos<<")"<<std::endl;
return ACCEPTABLE;
}
void Validator::fixup(std::string& text) const
{
const osg::CallbackObject* co = getCallbackObject(this, "fixup");
if (co)
{
osg::ref_ptr<osg::StringValueObject> textInput = new osg::StringValueObject("text",text);
osg::Parameters inputParameters, outputParameters;
inputParameters.push_back(textInput.get());
if (co->run(const_cast<Validator*>(this), inputParameters, outputParameters))
{
if (textInput->getValue()!=text)
{
OSG_NOTICE<<"Updating text in CallbackObject "<<textInput->getValue()<<std::endl;
text = textInput->getValue();
}
}
}
return fixupImplementation(text);
}
void Validator::fixupImplementation(std::string& text) const
{
OSG_NOTICE<<"Validator::fixupImplemetation("<<text<<")"<<std::endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IntValidator
//
IntValidator::IntValidator():
_bottom(-INT_MAX),
_top(-INT_MAX)
{
}
IntValidator::IntValidator(const IntValidator& validator, const osg::CopyOp& copyop):
Validator(validator, copyop),
_bottom(validator._bottom),
_top(validator._top)
{
}
IntValidator::State IntValidator::validateImplementation(std::string& str, int& cursorpos) const
{
std::string newstring;
bool canBeNegative = _bottom<0.0;
int numNegative = 0;
for(std::size_t pos = 0; pos<str.size(); ++pos)
{
char c = str[pos];
bool validChar = false;
if (c>='0' && c<='9')
{
validChar = true;
}
else if (c=='-')
{
if (canBeNegative)
{
if (numNegative==0) validChar = true;
++numNegative;
}
}
if (validChar) newstring.push_back(c);
}
str = newstring;
if (str.empty()) return INTERMEDIATE;
int v = static_cast<int>(osg::asciiToDouble(str.c_str()));
if (v<_bottom)
{
return INTERMEDIATE;
}
if (v>_top)
{
return INTERMEDIATE;
}
return ACCEPTABLE;
}
void IntValidator::fixupImplementation(std::string& str) const
{
if (str.empty()) return;
int v = static_cast<int>(osg::asciiToDouble(str.c_str()));
if (v<_bottom)
{
v = _bottom;
}
if (v>_top)
{
v = _top;
}
std::stringstream buffer;
buffer<<v<<std::endl;
str = buffer.str();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DoubleValidator
//
DoubleValidator::DoubleValidator():
_decimals(-1),
_bottom(-DBL_MAX),
_top(DBL_MAX)
{
}
DoubleValidator::DoubleValidator(const DoubleValidator& validator, const osg::CopyOp& copyop):
Validator(validator, copyop),
_decimals(validator._decimals),
_bottom(validator._bottom),
_top(validator._top)
{
}
DoubleValidator::State DoubleValidator::validateImplementation(std::string& str, int& cursorpos) const
{
std::string newstring;
bool canBeNegative = _bottom<0.0;
int maxNumDecimalPlaces = _decimals>=0 ? _decimals : str.size();
int numPlacesAfterDecimal = 0;
int numNegative = 0;
bool hasDecimal = false;
for(std::size_t pos = 0; pos<str.size(); ++pos)
{
char c = str[pos];
bool validChar = false;
if (c>='0' && c<='9')
{
if (hasDecimal)
{
++numPlacesAfterDecimal;
if (numPlacesAfterDecimal<=maxNumDecimalPlaces) validChar = true;
}
else
{
validChar = true;
}
}
else if (c=='-')
{
if (canBeNegative)
{
if (numNegative==0) validChar = true;
++numNegative;
}
}
else if (c=='.')
{
if (!hasDecimal)
{
validChar = true;
hasDecimal = true;
}
}
if (validChar) newstring.push_back(c);
}
str = newstring;
if (str.empty()) return INTERMEDIATE;
double v = osg::asciiToDouble(str.c_str());
if (v<_bottom)
{
return INTERMEDIATE;
}
if (v>_top)
{
return INTERMEDIATE;
}
return ACCEPTABLE;
}
void DoubleValidator::fixupImplementation(std::string& str) const
{
if (str.empty()) return;
double v = osg::asciiToDouble(str.c_str());
if (v<_bottom)
{
v = _bottom;
}
if (v>_top)
{
v = _top;
}
std::stringstream buffer;
buffer<<v<<std::endl;
str = buffer.str();
}

View File

@ -0,0 +1,17 @@
#include <osgUI/Validator>
#include <osg/ValueObject>
#include <osgDB/ObjectWrapper>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>
#include <limits.h>
REGISTER_OBJECT_WRAPPER( DoubleValidator,
new osgUI::DoubleValidator,
osgUI::DoubleValidator,
"osg::Object osgUI::Validator osgUI::DoubleValidator" )
{
ADD_INT_SERIALIZER(Decimals, -1);
ADD_DOUBLE_SERIALIZER(Bottom, -DBL_MAX);
ADD_DOUBLE_SERIALIZER(Top, DBL_MAX);
}

View File

@ -0,0 +1,16 @@
#include <osgUI/Validator>
#include <osg/ValueObject>
#include <osgDB/ObjectWrapper>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>
#include <limits.h>
REGISTER_OBJECT_WRAPPER( IntValidator,
new osgUI::IntValidator,
osgUI::IntValidator,
"osg::Object osgUI::Validator osgUI::IntValidator" )
{
ADD_INT_SERIALIZER(Bottom, -INT_MAX);
ADD_INT_SERIALIZER(Top, INT_MAX);
}

View File

@ -10,5 +10,6 @@ REGISTER_OBJECT_WRAPPER( LineEdit,
osgUI::LineEdit,
"osg::Object osg::Node osg::Group osgUI::Widget osgUI::LineEdit" )
{
ADD_OBJECT_SERIALIZER( Validator, osgUI::Validator, NULL);
ADD_STRING_SERIALIZER( Text, std::string());
}

View File

@ -0,0 +1,13 @@
#include <osgUI/Validator>
#include <osg/ValueObject>
#include <osgDB/ObjectWrapper>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>
REGISTER_OBJECT_WRAPPER( Validator,
new osgUI::Validator,
osgUI::Validator,
"osg::Object osgUI::Validator" )
{
}