phoenix-firestorm/indra/newview/fsvirtualtrackpad.cpp

464 lines
12 KiB
C++

/**
* @file irtualTrackpad.cpp
* @author Angeldark Raymaker; derived from llVirtualTrackball by Andrey Lihatskiy
* @brief Header file for FSVirtualTrackpad
*
* $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 two dimensional slider control with optional pinch-mode.
#include "fsvirtualtrackpad.h"
#include "llrect.h"
#include "lluictrlfactory.h"
// Globals
static LLDefaultChildRegistry::Register<FSVirtualTrackpad> register_virtual_trackball("fs_virtual_trackpad");
FSVirtualTrackpad::Params::Params()
: border("border"),
image_moon_back("image_moon_back"),
image_moon_front("image_moon_front"),
image_sphere("image_sphere"),
image_sun_back("image_sun_back"),
image_sun_front("image_sun_front"),
pinch_mode("pinch_mode"),
infinite_scroll_mode("infinite_scroll_mode")
{
}
FSVirtualTrackpad::FSVirtualTrackpad(const FSVirtualTrackpad::Params &p)
: LLUICtrl(p),
mImgMoonBack(p.image_moon_back),
mImgMoonFront(p.image_moon_front),
mImgSunBack(p.image_sun_back),
mImgSunFront(p.image_sun_front),
mImgSphere(p.image_sphere),
mAllowPinchMode(p.pinch_mode),
mInfiniteScrollMode(p.infinite_scroll_mode)
{
LLRect border_rect = getLocalRect();
_valueX = _pinchValueX = border_rect.getCenterX();
_valueY = _pinchValueY = border_rect.getCenterY();
_thumbClickOffsetX = _thumbClickOffsetY = _pinchThumbClickOffsetX = _pinchThumbClickOffsetY = 0;
_valueWheelClicks = _pinchValueWheelClicks = 0;
LLViewBorder::Params border = p.border;
border.rect(border_rect);
mBorder = LLUICtrlFactory::create<LLViewBorder>(border);
addChild(mBorder);
LLPanel::Params touch_area;
touch_area.rect = LLRect(border_rect);
mTouchArea = LLUICtrlFactory::create<LLPanel>(touch_area);
addChild(mTouchArea);
}
FSVirtualTrackpad::~FSVirtualTrackpad() {}
bool FSVirtualTrackpad::postBuild()
{
return true;
}
void FSVirtualTrackpad::drawThumb(bool isPinchThumb)
{
LLUIImage *thumb;
if (mTouchArea->isInEnabledChain())
thumb = isPinchThumb ? mImgSunFront : mImgMoonFront;
else
thumb = isPinchThumb ? mImgSunBack : mImgMoonBack;
S32 x = isPinchThumb ? _pinchValueX : _valueX;
S32 y = isPinchThumb ? _pinchValueY : _valueY;
wrapOrClipCursorPosition(&x, &y);
S32 halfThumbWidth = thumb->getWidth() / 2;
S32 halfThumbHeight = thumb->getHeight() / 2;
thumb->draw(LLRect(x - halfThumbWidth,
y + halfThumbHeight,
x + halfThumbWidth,
y - halfThumbHeight));
}
bool FSVirtualTrackpad::isPointInTouchArea(S32 x, S32 y) const
{
if (!mTouchArea)
return false;
return mTouchArea->getRect().localPointInRect(x, y);
}
void FSVirtualTrackpad::determineThumbClickError(S32 x, S32 y)
{
if (!mTouchArea)
return;
if (!mImgSunFront)
return;
_thumbClickOffsetX = 0;
_thumbClickOffsetY = 0;
S32 errorX = _valueX;
S32 errorY = _valueY;
wrapOrClipCursorPosition(&errorX, &errorY);
errorX -= x;
errorY -= y;
if (fabs(errorX) > mImgSunFront->getWidth() / 2.0)
return;
if (fabs(errorY) > mImgSunFront->getHeight() / 2.0)
return;
_thumbClickOffsetX = errorX;
_thumbClickOffsetY = errorY;
}
void FSVirtualTrackpad::updateClickErrorIfInfiniteScrolling()
{
if (!mInfiniteScrollMode)
return;
S32 errorX = _valueX;
S32 errorY = _valueY;
LLRect rect = mTouchArea->getRect();
while (errorX > rect.mRight)
{
errorX -= rect.getWidth();
_thumbClickOffsetX += rect.getWidth();
}
while (errorX < rect.mLeft)
{
errorX += rect.getWidth();
_thumbClickOffsetX -= rect.getWidth();
}
while (errorY > rect.mTop)
{
errorY -= rect.getHeight();
_thumbClickOffsetY += rect.getHeight();
}
while (errorY < rect.mBottom)
{
errorY += rect.getHeight();
_thumbClickOffsetY -= rect.getHeight();
}
}
void FSVirtualTrackpad::determineThumbClickErrorForPinch(S32 x, S32 y)
{
if (!mTouchArea)
return;
if (!mImgMoonFront)
return;
_pinchThumbClickOffsetX = 0;
_pinchThumbClickOffsetY = 0;
S32 errorX = _pinchValueX;
S32 errorY = _pinchValueY;
wrapOrClipCursorPosition(&errorX, &errorY);
errorX -= x;
errorY -= y;
if (fabs(errorX) > mImgMoonFront->getWidth() / 2.0)
return;
if (fabs(errorY) > mImgMoonFront->getHeight() / 2.0)
return;
_pinchThumbClickOffsetX = errorX;
_pinchThumbClickOffsetY = errorY;
}
void FSVirtualTrackpad::updateClickErrorIfInfiniteScrollingForPinch()
{
if (!mInfiniteScrollMode)
return;
S32 errorX = _valueX;
S32 errorY = _valueY;
LLRect rect = mTouchArea->getRect();
while (errorX > rect.mRight)
{
errorX -= rect.getWidth();
_pinchThumbClickOffsetX += rect.getWidth();
}
while (errorX < rect.mLeft)
{
errorX += rect.getWidth();
_pinchThumbClickOffsetX -= rect.getWidth();
}
while (errorY > rect.mTop)
{
errorY -= rect.getHeight();
_pinchThumbClickOffsetY += rect.getHeight();
}
while (errorY < rect.mBottom)
{
errorY += rect.getHeight();
_pinchThumbClickOffsetY -= rect.getHeight();
}
}
void FSVirtualTrackpad::draw()
{
mImgSphere->draw(mTouchArea->getRect(), mTouchArea->isInEnabledChain() ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
if (mAllowPinchMode)
drawThumb(true);
drawThumb(false);
LLView::draw();
}
void FSVirtualTrackpad::setValue(const LLSD& value)
{
if (value.isArray() && value.size() == 2)
{
LLVector2 vec2;
vec2.setValue(value);
setValue(vec2.mV[VX], vec2.mV[VY]);
}
}
void FSVirtualTrackpad::setValue(F32 x, F32 y) { convertNormalizedToPixelPos(x, y, &_valueX, &_valueY); }
void FSVirtualTrackpad::setPinchValue(F32 x, F32 y) { convertNormalizedToPixelPos(x, y, &_pinchValueX, &_pinchValueY); }
void FSVirtualTrackpad::setValueAndCommit(const S32 x, const S32 y)
{
_valueX = x;
_valueY = y;
_valueWheelClicks = -1 * _wheelClicksSinceMouseDown;
_wheelClicksSinceMouseDown = 0;
onCommit();
}
void FSVirtualTrackpad::setPinchValueAndCommit(const S32 x, const S32 y)
{
_pinchValueX = x;
_pinchValueY = y;
_pinchValueWheelClicks = -1 * _wheelClicksSinceMouseDown;
_wheelClicksSinceMouseDown = 0;
onCommit();
}
LLSD FSVirtualTrackpad::getValue()
{
LLSD result = normalizePixelPos(_valueX, _valueY, _valueWheelClicks).getValue();
_valueWheelClicks = 0;
return result;
}
LLSD FSVirtualTrackpad::getPinchValue()
{
LLSD result = normalizePixelPos(_pinchValueX, _pinchValueY, _pinchValueWheelClicks).getValue();
_pinchValueWheelClicks = 0;
return result;
}
void FSVirtualTrackpad::wrapOrClipCursorPosition(S32* x, S32* y)
{
if (!x || !y)
return;
LLRect rect = mTouchArea->getRect();
if (mInfiniteScrollMode)
{
while (*x > rect.mRight)
*x -= rect.getWidth();
while (*x < rect.mLeft)
*x += rect.getWidth();
while (*y > rect.mTop)
*y -= rect.getHeight();
while (*y < rect.mBottom)
*y += rect.getHeight();
}
else
{
rect.clampPointToRect(*x, *y);
}
}
bool FSVirtualTrackpad::handleHover(S32 x, S32 y, MASK mask)
{
if (!hasMouseCapture())
return true;
S32 correctedX, correctedY;
if (doingPinchMode)
{
correctedX = x + _pinchThumbClickOffsetX;
correctedY = y + _pinchThumbClickOffsetY;
}
else
{
correctedX = x + _thumbClickOffsetX;
correctedY = y + _thumbClickOffsetY;
}
if (!mInfiniteScrollMode)
wrapOrClipCursorPosition(&correctedX, &correctedY);
if (doingPinchMode)
{
_pinchValueX = correctedX;
_pinchValueY = correctedY;
}
else
{
_valueX = correctedX;
_valueY = correctedY;
}
onCommit();
return true;
}
LLVector3 FSVirtualTrackpad::normalizePixelPos(S32 x, S32 y, S32 z) const
{
LLVector3 result;
if (!mTouchArea)
return result;
LLRect rect = mTouchArea->getRect();
S32 centerX = rect.getCenterX();
S32 centerY = rect.getCenterY();
S32 width = rect.getWidth();
S32 height = rect.getHeight();
result.mV[VX] = (F32) (x - centerX) / width * 2;
result.mV[VY] = (F32) (y - centerY) / height * 2;
result.mV[VZ] = (F32) z;
return result;
}
void FSVirtualTrackpad::convertNormalizedToPixelPos(F32 x, F32 y, S32 *valX, S32 *valY)
{
if (!mTouchArea)
return;
LLRect rect = mTouchArea->getRect();
S32 centerX = rect.getCenterX();
S32 centerY = rect.getCenterY();
S32 width = rect.getWidth();
S32 height = rect.getHeight();
if (mInfiniteScrollMode)
{
*valX = centerX + ll_round(x * width / 2);
*valY = centerY + ll_round(y * height / 2);
}
else
{
*valX = centerX + ll_round(llclamp(x, -1, 1) * width / 2);
*valY = centerY + ll_round(llclamp(y, -1, 1) * height / 2);
}
}
bool FSVirtualTrackpad::handleMouseUp(S32 x, S32 y, MASK mask)
{
if (hasMouseCapture())
{
doingPinchMode = false;
gFocusMgr.setMouseCapture(NULL);
make_ui_sound("UISndClickRelease");
}
return LLView::handleMouseUp(x, y, mask);
}
bool FSVirtualTrackpad::handleMouseDown(S32 x, S32 y, MASK mask)
{
if (isPointInTouchArea(x, y))
{
determineThumbClickError(x, y);
updateClickErrorIfInfiniteScrolling();
_valueWheelClicks = 0;
_wheelClicksSinceMouseDown = 0;
gFocusMgr.setMouseCapture(this);
make_ui_sound("UISndClick");
}
return LLView::handleMouseDown(x, y, mask);
}
bool FSVirtualTrackpad::handleRightMouseUp(S32 x, S32 y, MASK mask)
{
if (hasMouseCapture())
{
doingPinchMode = false;
gFocusMgr.setMouseCapture(NULL);
make_ui_sound("UISndClickRelease");
}
return LLView::handleRightMouseUp(x, y, mask);
}
// move pinch cursor
bool FSVirtualTrackpad::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
if (!mAllowPinchMode)
return LLView::handleRightMouseDown(x, y, mask);
if (isPointInTouchArea(x, y))
{
determineThumbClickErrorForPinch(x, y);
updateClickErrorIfInfiniteScrollingForPinch();
_pinchValueWheelClicks = 0;
_wheelClicksSinceMouseDown = 0;
doingPinchMode = true;
gFocusMgr.setMouseCapture(this);
make_ui_sound("UISndClick");
}
return LLView::handleRightMouseDown(x, y, mask);
}
// pass wheel-clicks to third axis
bool FSVirtualTrackpad::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
if (hasMouseCapture())
{
if (doingPinchMode)
_pinchValueWheelClicks = clicks;
else
_valueWheelClicks = clicks;
_wheelClicksSinceMouseDown += clicks;
return true;
}
else
return LLUICtrl::handleScrollWheel(x, y, clicks);
}