Files
SingularityViewer/indra/llui/llspinctrl.cpp
Beeks d49c92b6fe I got bored and put in the math functions from Imprudence.
We need something to show the list - Preferably a bit better than the popup that they use, I'm thinking maybe an expanding sidebar on the build menu?

Oh, minor changes - Texture repeats go up to 1000 now because it has numbers, and I tweaked the primitive params section so it doesn't fall off the window.

Signed-off-by: Beeks <HgDelirium@gmail.com>
2010-09-16 22:08:33 -04:00

589 lines
13 KiB
C++

/**
* @file llspinctrl.cpp
* @brief LLSpinCtrl base class
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llspinctrl.h"
#include "llgl.h"
#include "llui.h"
#include "lluiconstants.h"
#include "llstring.h"
#include "llfontgl.h"
#include "lllineeditor.h"
#include "llbutton.h"
#include "lltextbox.h"
#include "llkeyboard.h"
#include "llmath.h"
#include "llcontrol.h"
#include "llfocusmgr.h"
#include "llresmgr.h"
const U32 MAX_STRING_LENGTH = 32;
static LLRegisterWidget<LLSpinCtrl> r2("spinner");
LLSpinCtrl::LLSpinCtrl( const std::string& name, const LLRect& rect, const std::string& label, const LLFontGL* font,
void (*commit_callback)(LLUICtrl*, void*),
void* callback_user_data,
F32 initial_value, F32 min_value, F32 max_value, F32 increment,
const std::string& control_name,
S32 label_width)
:
LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data, FOLLOWS_LEFT | FOLLOWS_TOP ),
mValue( initial_value ),
mInitialValue( initial_value ),
mMaxValue( max_value ),
mMinValue( min_value ),
mIncrement( increment ),
mPrecision( 3 ),
mLabelBox( NULL ),
mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
mbHasBeenSet( FALSE )
{
S32 top = getRect().getHeight();
S32 bottom = top - 2 * SPINCTRL_BTN_HEIGHT;
S32 centered_top = top;
S32 centered_bottom = bottom;
S32 btn_left = 0;
// Label
if( !label.empty() )
{
LLRect label_rect( 0, centered_top, label_width, centered_bottom );
mLabelBox = new LLTextBox( std::string("SpinCtrl Label"), label_rect, label, font );
addChild(mLabelBox);
btn_left += label_rect.mRight + SPINCTRL_SPACING;
}
S32 btn_right = btn_left + SPINCTRL_BTN_WIDTH;
// Spin buttons
LLRect up_rect( btn_left, top, btn_right, top - SPINCTRL_BTN_HEIGHT );
std::string out_id = "UIImgBtnSpinUpOutUUID";
std::string in_id = "UIImgBtnSpinUpInUUID";
mUpBtn = new LLButton(std::string("SpinCtrl Up"), up_rect,
out_id,
in_id,
LLStringUtil::null,
&LLSpinCtrl::onUpBtn, this, LLFontGL::getFontSansSerif() );
mUpBtn->setFollowsLeft();
mUpBtn->setFollowsBottom();
mUpBtn->setHeldDownCallback( &LLSpinCtrl::onUpBtn );
mUpBtn->setTabStop(FALSE);
addChild(mUpBtn);
LLRect down_rect( btn_left, top - SPINCTRL_BTN_HEIGHT, btn_right, bottom );
out_id = "UIImgBtnSpinDownOutUUID";
in_id = "UIImgBtnSpinDownInUUID";
mDownBtn = new LLButton(std::string("SpinCtrl Down"), down_rect,
out_id,
in_id,
LLStringUtil::null,
&LLSpinCtrl::onDownBtn, this, LLFontGL::getFontSansSerif() );
mDownBtn->setFollowsLeft();
mDownBtn->setFollowsBottom();
mDownBtn->setHeldDownCallback( &LLSpinCtrl::onDownBtn );
mDownBtn->setTabStop(FALSE);
addChild(mDownBtn);
LLRect editor_rect( btn_right + 1, centered_top, getRect().getWidth(), centered_bottom );
mEditor = new LLLineEditor( std::string("SpinCtrl Editor"), editor_rect, LLStringUtil::null, font,
MAX_STRING_LENGTH,
&LLSpinCtrl::onEditorCommit, NULL, NULL, this,
&LLLineEditor::prevalidateASCII );
mEditor->setFollowsLeft();
mEditor->setFollowsBottom();
mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus, this );
//RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus
// than when it doesn't. Instead, if you always have to double click to select all the text,
// it's easier to understand
//mEditor->setSelectAllonFocusReceived(TRUE);
mEditor->setIgnoreTab(TRUE);
mEditor->setSelectAllonCommit(FALSE);
addChild(mEditor);
updateEditor();
setUseBoundingRect( TRUE );
}
F32 clamp_precision(F32 value, S32 decimal_precision)
{
// pow() isn't perfect
F64 clamped_value = value;
for (S32 i = 0; i < decimal_precision; i++)
clamped_value *= 10.0;
clamped_value = llround((F32)clamped_value);
for (S32 i = 0; i < decimal_precision; i++)
clamped_value /= 10.0;
return (F32)clamped_value;
}
// static
void LLSpinCtrl::onUpBtn( void *userdata )
{
LLSpinCtrl* self = (LLSpinCtrl*) userdata;
if( self->getEnabled() )
{
// use getValue()/setValue() to force reload from/to control
F32 val = (F32)self->getValue().asReal() + self->mIncrement;
val = clamp_precision(val, self->mPrecision);
val = llmin( val, self->mMaxValue );
if( self->mValidateCallback )
{
F32 saved_val = (F32)self->getValue().asReal();
self->setValue(val);
if( !self->mValidateCallback( self, self->mCallbackUserData ) )
{
self->setValue( saved_val );
self->reportInvalidData();
self->updateEditor();
return;
}
}
else
{
self->setValue(val);
}
self->updateEditor();
self->onCommit();
}
}
// static
void LLSpinCtrl::onDownBtn( void *userdata )
{
LLSpinCtrl* self = (LLSpinCtrl*) userdata;
if( self->getEnabled() )
{
F32 val = (F32)self->getValue().asReal() - self->mIncrement;
val = clamp_precision(val, self->mPrecision);
val = llmax( val, self->mMinValue );
if( self->mValidateCallback )
{
F32 saved_val = (F32)self->getValue().asReal();
self->setValue(val);
if( !self->mValidateCallback( self, self->mCallbackUserData ) )
{
self->setValue( saved_val );
self->reportInvalidData();
self->updateEditor();
return;
}
}
else
{
self->setValue(val);
}
self->updateEditor();
self->onCommit();
}
}
// static
void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
{
LLSpinCtrl* self = (LLSpinCtrl*) userdata;
llassert( caller == self->mEditor );
self->onFocusReceived();
}
void LLSpinCtrl::setValue(const LLSD& value )
{
F32 v = (F32)value.asReal();
if (mValue != v || !mbHasBeenSet)
{
mbHasBeenSet = TRUE;
mValue = v;
if (!mEditor->hasFocus())
{
updateEditor();
}
}
}
//no matter if Editor has the focus, update the value
void LLSpinCtrl::forceSetValue(const LLSD& value )
{
F32 v = (F32)value.asReal();
if (mValue != v || !mbHasBeenSet)
{
mbHasBeenSet = TRUE;
mValue = v;
updateEditor();
}
}
void LLSpinCtrl::clear()
{
setValue(mMinValue);
mEditor->clear();
mbHasBeenSet = FALSE;
}
void LLSpinCtrl::updateEditor()
{
LLLocale locale(LLLocale::USER_LOCALE);
// Don't display very small negative values as -0.000
F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
// {
// displayed_value = 0.f;
// }
std::string format = llformat("%%.%df", mPrecision);
std::string text = llformat(format.c_str(), displayed_value);
mEditor->setText( text );
}
void LLSpinCtrl::onEditorCommit( LLUICtrl* caller, void *userdata )
{
BOOL success = FALSE;
LLSpinCtrl* self = (LLSpinCtrl*) userdata;
llassert( caller == self->mEditor );
if( self->mEditor->evaluateFloat() )
{
std::string text = self->mEditor->getText();
LLLocale locale(LLLocale::USER_LOCALE);
F32 val = (F32) atof(text.c_str());
if (val < self->mMinValue) val = self->mMinValue;
if (val > self->mMaxValue) val = self->mMaxValue;
if( self->mValidateCallback )
{
F32 saved_val = self->mValue;
self->mValue = val;
if( self->mValidateCallback( self, self->mCallbackUserData ) )
{
success = TRUE;
self->onCommit();
}
else
{
self->mValue = saved_val;
}
}
else
{
self->mValue = val;
self->onCommit();
success = TRUE;
}
}
else
{
// We want to update the editor in case it fails while blanking -- MC
success = TRUE;
}
if( success )
{
self->updateEditor();
}
else
{
self->reportInvalidData();
}
}
void LLSpinCtrl::forceEditorCommit()
{
onEditorCommit(mEditor, this);
}
void LLSpinCtrl::setFocus(BOOL b)
{
LLUICtrl::setFocus( b );
mEditor->setFocus( b );
}
void LLSpinCtrl::setEnabled(BOOL b)
{
LLView::setEnabled( b );
mEditor->setEnabled( b );
}
void LLSpinCtrl::setTentative(BOOL b)
{
mEditor->setTentative(b);
LLUICtrl::setTentative(b);
}
BOOL LLSpinCtrl::isMouseHeldDown() const
{
return
mDownBtn->hasMouseCapture()
|| mUpBtn->hasMouseCapture();
}
void LLSpinCtrl::onCommit()
{
setTentative(FALSE);
setControlValue(mValue);
LLUICtrl::onCommit();
}
void LLSpinCtrl::setPrecision(S32 precision)
{
if (precision < 0 || precision > 10)
{
llerrs << "LLSpinCtrl::setPrecision - precision out of range" << llendl;
return;
}
mPrecision = precision;
updateEditor();
}
void LLSpinCtrl::setLabel(const LLStringExplicit& label)
{
if (mLabelBox)
{
mLabelBox->setText(label);
}
else
{
llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl;
}
}
BOOL LLSpinCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
{
if (mLabelBox)
{
BOOL res = mLabelBox->setTextArg(key, text);
reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
return res;
}
return FALSE;
}
void LLSpinCtrl::setAllowEdit(BOOL allow_edit)
{
mEditor->setEnabled(allow_edit);
mAllowEdit = allow_edit;
}
void LLSpinCtrl::onTabInto()
{
mEditor->onTabInto();
}
void LLSpinCtrl::reportInvalidData()
{
make_ui_sound("UISndBadKeystroke");
}
void LLSpinCtrl::draw()
{
if( mLabelBox )
{
mLabelBox->setColor( getEnabled() ? mTextEnabledColor : mTextDisabledColor );
}
LLUICtrl::draw();
}
BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
if( clicks > 0 )
{
while( clicks-- )
{
LLSpinCtrl::onDownBtn(this);
}
}
else
while( clicks++ )
{
LLSpinCtrl::onUpBtn(this);
}
return TRUE;
}
BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask)
{
if (mEditor->hasFocus())
{
if (key == KEY_ESCAPE && mask == MASK_NONE)
{
// text editors don't support revert normally (due to user confusion)
// but not allowing revert on a spinner seems dangerous
updateEditor();
mEditor->setFocus(FALSE);
return TRUE;
}
if(key == KEY_UP)
{
LLSpinCtrl::onUpBtn(this);
return TRUE;
}
if(key == KEY_DOWN)
{
LLSpinCtrl::onDownBtn(this);
return TRUE;
}
if(key == KEY_RETURN)
{
forceEditorCommit();
return TRUE;
}
}
return FALSE;
}
// virtual
LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const
{
LLXMLNodePtr node = LLUICtrl::getXML();
node->setName(LL_SPIN_CTRL_TAG);
node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision);
if (mLabelBox)
{
node->createChild("label", TRUE)->setStringValue(mLabelBox->getText());
node->createChild("label_width", TRUE)->setIntValue(mLabelBox->getRect().getWidth());
}
node->createChild("initial_val", TRUE)->setFloatValue(mInitialValue);
node->createChild("min_val", TRUE)->setFloatValue(mMinValue);
node->createChild("max_val", TRUE)->setFloatValue(mMaxValue);
node->createChild("increment", TRUE)->setFloatValue(mIncrement);
addColorXML(node, mTextEnabledColor, "text_enabled_color", "LabelTextColor");
addColorXML(node, mTextDisabledColor, "text_disabled_color", "LabelDisabledColor");
return node;
}
LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
{
std::string name("spinner");
node->getAttributeString("name", name);
std::string label;
node->getAttributeString("label", label);
LLRect rect;
createRect(node, rect, parent, LLRect());
LLFontGL* font = LLView::selectFont(node);
F32 initial_value = 0.f;
node->getAttributeF32("initial_val", initial_value);
F32 min_value = 0.f;
node->getAttributeF32("min_val", min_value);
F32 max_value = 1.f;
node->getAttributeF32("max_val", max_value);
F32 increment = 0.1f;
node->getAttributeF32("increment", increment);
U32 precision = 3;
node->getAttributeU32("decimal_digits", precision);
S32 label_width = llmin(40, rect.getWidth() - 40);
node->getAttributeS32("label_width", label_width);
BOOL allow_text_entry = TRUE;
node->getAttributeBOOL("allow_text_entry", allow_text_entry);
LLUICtrlCallback callback = NULL;
if(label.empty())
{
label.assign( node->getValue() );
}
LLSpinCtrl* spinner = new LLSpinCtrl(name,
rect,
label,
font,
callback,
NULL,
initial_value,
min_value,
max_value,
increment,
LLStringUtil::null,
label_width);
spinner->setPrecision(precision);
spinner->initFromXML(node, parent);
spinner->setAllowEdit(allow_text_entry);
return spinner;
}