diff options
Diffstat (limited to 'indra/llui/llxyvector.cpp')
-rw-r--r-- | indra/llui/llxyvector.cpp | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/indra/llui/llxyvector.cpp b/indra/llui/llxyvector.cpp new file mode 100644 index 0000000000..d7ba243e1d --- /dev/null +++ b/indra/llui/llxyvector.cpp @@ -0,0 +1,337 @@ +/** +* @file llxyvector.cpp +* @author Andrey Lihatskiy +* @brief Implementation for LLXYVector +* +* $LicenseInfo:firstyear=2001&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* 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 GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +// A control that allows to set two related vector magnitudes by manipulating a single vector on a plane. + +#include "linden_common.h" + +#include "llxyvector.h" + +#include "llstring.h" +#include "llrect.h" + +#include "lluictrlfactory.h" +#include "llrender.h" + +#include "llmath.h" + +// Globals +static LLDefaultChildRegistry::Register<LLXYVector> register_xy_vector("xy_vector"); + + +const F32 CENTER_CIRCLE_RADIUS = 2; +const S32 ARROW_ANGLE = 30; +const S32 ARROW_LENGTH_LONG = 10; +const S32 ARROW_LENGTH_SHORT = 6; + +LLXYVector::Params::Params() + : x_entry("x_entry"), + y_entry("y_entry"), + touch_area("touch_area"), + border("border"), + edit_bar_height("edit_bar_height", 18), + padding("padding", 4), + min_val_x("min_val_x", -1.0f), + max_val_x("max_val_x", 1.0f), + increment_x("increment_x", 0.05f), + min_val_y("min_val_y", -1.0f), + max_val_y("max_val_y", 1.0f), + increment_y("increment_y", 0.05f), + label_width("label_width", 16), + arrow_color("arrow_color", LLColor4::white), + ghost_color("ghost_color"), + area_color("area_color", LLColor4::grey4), + grid_color("grid_color", LLColor4::grey % 0.25f), + logarithmic("logarithmic", FALSE) +{ +} + +LLXYVector::LLXYVector(const LLXYVector::Params& p) + : LLUICtrl(p), + mArrowColor(p.arrow_color()), + mAreaColor(p.area_color), + mGridColor(p.grid_color), + mMinValueX(p.min_val_x), + mMaxValueX(p.max_val_x), + mIncrementX(p.increment_x), + mMinValueY(p.min_val_y), + mMaxValueY(p.max_val_y), + mIncrementY(p.increment_y), + mLogarithmic(p.logarithmic), + mValueX(0), + mValueY(0) +{ + mGhostColor = p.ghost_color.isProvided() ? p.ghost_color() % 0.3f : p.arrow_color() % 0.3f; + + LLRect border_rect = getLocalRect(); + LLViewBorder::Params params = p.border; + params.rect(border_rect); + mBorder = LLUICtrlFactory::create<LLViewBorder>(params); + addChild(mBorder); + + LLTextBox::Params x_label_params; + x_label_params.initial_value(p.x_entry.label()); + x_label_params.rect = LLRect(p.padding, + border_rect.mTop - p.padding, + p.label_width, + border_rect.getHeight() - p.edit_bar_height); + mXLabel = LLUICtrlFactory::create<LLTextBox>(x_label_params); + addChild(mXLabel); + LLLineEditor::Params x_params = p.x_entry; + x_params.rect = LLRect(p.padding + p.label_width, + border_rect.mTop - p.padding, + border_rect.getCenterX(), + border_rect.getHeight() - p.edit_bar_height); + x_params.commit_callback.function(boost::bind(&LLXYVector::onEditChange, this)); + mXEntry = LLUICtrlFactory::create<LLLineEditor>(x_params); + mXEntry->setPrevalidateInput(LLTextValidate::validateFloat); + addChild(mXEntry); + + LLTextBox::Params y_label_params; + y_label_params.initial_value(p.y_entry.label()); + y_label_params.rect = LLRect(border_rect.getCenterX() + p.padding, + border_rect.mTop - p.padding, + border_rect.getCenterX() + p.label_width, + border_rect.getHeight() - p.edit_bar_height); + mYLabel = LLUICtrlFactory::create<LLTextBox>(y_label_params); + addChild(mYLabel); + LLLineEditor::Params y_params = p.y_entry; + y_params.rect = LLRect(border_rect.getCenterX() + p.padding + p.label_width, + border_rect.getHeight() - p.padding, + border_rect.getWidth() - p.padding, + border_rect.getHeight() - p.edit_bar_height); + y_params.commit_callback.function(boost::bind(&LLXYVector::onEditChange, this)); + mYEntry = LLUICtrlFactory::create<LLLineEditor>(y_params); + mYEntry->setPrevalidateInput(LLTextValidate::validateFloat); + addChild(mYEntry); + + LLPanel::Params touch_area = p.touch_area; + touch_area.rect = LLRect(p.padding, + border_rect.mTop - p.edit_bar_height - p.padding, + border_rect.getWidth() - p.padding, + p.padding); + mTouchArea = LLUICtrlFactory::create<LLPanel>(touch_area); + addChild(mTouchArea); +} + +LLXYVector::~LLXYVector() +{ +} + +BOOL LLXYVector::postBuild() +{ + mLogScaleX = (2 * log(mMaxValueX)) / mTouchArea->getRect().getWidth(); + mLogScaleY = (2 * log(mMaxValueY)) / mTouchArea->getRect().getHeight(); + + return TRUE; +} + +void drawArrow(S32 tailX, S32 tailY, S32 tipX, S32 tipY, LLColor4 color) +{ + gl_line_2d(tailX, tailY, tipX, tipY, color); + + S32 dx = tipX - tailX; + S32 dy = tipY - tailY; + + S32 arrowLength = (abs(dx) < ARROW_LENGTH_LONG && abs(dy) < ARROW_LENGTH_LONG) ? ARROW_LENGTH_SHORT : ARROW_LENGTH_LONG; + + F32 theta = std::atan2(dy, dx); + + F32 rad = ARROW_ANGLE * std::atan(1) * 4 / 180; + F32 x = tipX - arrowLength * cos(theta + rad); + F32 y = tipY - arrowLength * sin(theta + rad); + F32 rad2 = -1 * ARROW_ANGLE * std::atan(1) * 4 / 180; + F32 x2 = tipX - arrowLength * cos(theta + rad2); + F32 y2 = tipY - arrowLength * sin(theta + rad2); + gl_triangle_2d(tipX, tipY, x, y, x2, y2, color, true); +} + +void LLXYVector::draw() +{ + S32 centerX = mTouchArea->getRect().getCenterX(); + S32 centerY = mTouchArea->getRect().getCenterY(); + S32 pointX; + S32 pointY; + + if (mLogarithmic) + { + pointX = (log(llabs(mValueX) + 1)) / mLogScaleX; + pointX *= (mValueX < 0) ? -1 : 1; + pointX += centerX; + + pointY = (log(llabs(mValueY) + 1)) / mLogScaleY; + pointY *= (mValueY < 0) ? -1 : 1; + pointY += centerY; + } + else // linear + { + pointX = centerX + (mValueX * mTouchArea->getRect().getWidth() / (2 * mMaxValueX)); + pointY = centerY + (mValueY * mTouchArea->getRect().getHeight() / (2 * mMaxValueY)); + } + + // fill + gl_rect_2d(mTouchArea->getRect(), mAreaColor, true); + + // draw grid + gl_line_2d(centerX, mTouchArea->getRect().mTop, centerX, mTouchArea->getRect().mBottom, mGridColor); + gl_line_2d(mTouchArea->getRect().mLeft, centerY, mTouchArea->getRect().mRight, centerY, mGridColor); + + // draw ghost + if (hasMouseCapture()) + { + drawArrow(centerX, centerY, mGhostX, mGhostY, mGhostColor); + } + else + { + mGhostX = pointX; + mGhostY = pointY; + } + + if (abs(mValueX) >= mIncrementX || abs(mValueY) >= mIncrementY) + { + // draw the vector arrow + drawArrow(centerX, centerY, pointX, pointY, mArrowColor); + } + else + { + // skip the arrow, set color for center circle + gGL.color4fv(mArrowColor.get().mV); + } + + // draw center circle + gl_circle_2d(centerX, centerY, CENTER_CIRCLE_RADIUS, 12, true); + + LLView::draw(); +} + +void LLXYVector::onEditChange() +{ + if (getEnabled()) + { + setValueAndCommit(mXEntry->getValue().asReal(), mYEntry->getValue().asReal()); + } +} + +void LLXYVector::setValue(const LLSD& value) +{ + if (value.isArray()) + { + setValue(value[0].asReal(), value[1].asReal()); + } +} + +void LLXYVector::setValue(F32 x, F32 y) +{ + mValueX = ll_round(llclamp(x, mMinValueX, mMaxValueX), mIncrementX); + mValueY = ll_round(llclamp(y, mMinValueY, mMaxValueY), mIncrementY); + + update(); +} + +void LLXYVector::setValueAndCommit(F32 x, F32 y) +{ + if (mValueX != x || mValueY != y) + { + setValue(x, y); + onCommit(); + } +} + +LLSD LLXYVector::getValue() const +{ + LLSD value; + value.append(mValueX); + value.append(mValueY); + return value; +} + +void LLXYVector::update() +{ + mXEntry->setValue(mValueX); + mYEntry->setValue(mValueY); +} + +BOOL LLXYVector::handleHover(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + if (mLogarithmic) + { + F32 valueX = llfastpow(F_E, mLogScaleX*(llabs(x - mTouchArea->getRect().getCenterX()))) - 1; + valueX *= (x < mTouchArea->getRect().getCenterX()) ? -1 : 1; + + F32 valueY = llfastpow(F_E, mLogScaleY*(llabs(y - mTouchArea->getRect().getCenterY()))) - 1; + valueY *= (y < mTouchArea->getRect().getCenterY()) ? -1 : 1; + + setValueAndCommit(valueX, valueY); + } + else //linear + { + F32 valueX = 2 * mMaxValueX * F32(x - mTouchArea->getRect().getCenterX()) / mTouchArea->getRect().getWidth(); + F32 valueY = 2 * mMaxValueY * F32(y - mTouchArea->getRect().getCenterY()) / mTouchArea->getRect().getHeight(); + + setValueAndCommit(valueX, valueY); + } + } + + return TRUE; +} + +BOOL LLXYVector::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + make_ui_sound("UISndClickRelease"); + } + + if (mTouchArea->getRect().pointInRect(x, y)) + { + return TRUE; + } + else + { + return LLUICtrl::handleMouseUp(x, y, mask); + } +} + +BOOL LLXYVector::handleMouseDown(S32 x, S32 y, MASK mask) +{ + + if (mTouchArea->getRect().pointInRect(x, y)) + { + gFocusMgr.setMouseCapture(this); + make_ui_sound("UISndClick"); + + return TRUE; + } + else + { + return LLUICtrl::handleMouseDown(x, y, mask); + } +} + |