summaryrefslogtreecommitdiff
path: root/indra/llui/llxyvector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llxyvector.cpp')
-rw-r--r--indra/llui/llxyvector.cpp337
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);
+ }
+}
+