123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- /**
- * @file llvirtualtrackball.cpp
- * @brief LLVirtualTrackball class definition
- *
- * $LicenseInfo:firstyear=2018&license=viewergpl$
- *
- * Copyright (c) 2018, 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 "llvirtualtrackball.h"
- #include "llbutton.h"
- #include "llpanel.h"
- #include "llrender.h"
- #include "lltextbox.h"
- static const std::string LL_SUN_MOON_TRACKBALL_TAG = "sun_moon_trackball";
- static LLRegisterWidget<LLVirtualTrackball> r33(LL_SUN_MOON_TRACKBALL_TAG);
- // UI elements constants
- constexpr S32 TRACKBALL_WIDTH = 150;
- constexpr S32 TRACKBALL_HEIGHT = 150;
- constexpr S32 TRACKBALL_BTN_SIZE = 32; // Width & height
- constexpr S32 TRACKBALL_AXIS_OFFSET = 16; // Offset from left/top sides
- const std::string TRACKBALL_LABEL_N = "N";
- const std::string TRACKBALL_LABEL_S = "S";
- const std::string TRACKBALL_LABEL_W = "E";
- const std::string TRACKBALL_LABEL_E = "W";
- LLVirtualTrackball::LLVirtualTrackball(const std::string& name,
- const LLRect& ui_rect,
- void (*commit_cb)(LLUICtrl*, void*),
- void* userdata)
- : LLUICtrl(name, ui_rect, true, commit_cb, userdata,
- FOLLOWS_TOP | FOLLOWS_LEFT),
- mPrevX(0),
- mPrevY(0),
- mIncrementMouse(DEG_TO_RAD * 0.5f),
- mIncrementBtn(DEG_TO_RAD * 3.f),
- mThumbMode(SUN),
- mImgMoonBack(LLUI::getUIImage("track_control_moon_back.png")),
- mImgMoonFront(LLUI::getUIImage("track_control_moon_front.png")),
- mImgSunBack(LLUI::getUIImage("track_control_sun_back.png")),
- mImgSunFront(LLUI::getUIImage("track_control_sun_front.png")),
- mImgSphere(LLUI::getUIImage("track_control_sphere.png"))
- {
- LLRect border_rect = getLocalRect();
- mBorder = new LLViewBorder("border", border_rect);
- addChild(mBorder);
- S32 center_x = border_rect.getCenterX();
- S32 center_y = border_rect.getCenterY();
- // Offset from the axis for left/top sides
- S32 axis_offset_rb = TRACKBALL_BTN_SIZE - TRACKBALL_AXIS_OFFSET;
- LLRect rect = LLRect(center_x - TRACKBALL_AXIS_OFFSET, border_rect.mTop,
- center_x + axis_offset_rb,
- border_rect.mTop - TRACKBALL_BTN_SIZE);
- mBtnRotateTop = new LLButton("button_rotate_top", rect,
- "track_control_rotate_top.png",
- "track_control_rotate_top_active.png",
- "", onRotateTopClick, this);
- mBtnRotateTop->setHeldDownCallback(onRotateTopClickNoSound);
- addChild(mBtnRotateTop);
- rect.translate(0, -TRACKBALL_BTN_SIZE / 2);
- mLabelN = new LLTextBox("labelN", rect, TRACKBALL_LABEL_N);
- addChild(mLabelN);
- rect = LLRect(border_rect.mRight - TRACKBALL_BTN_SIZE,
- center_y + TRACKBALL_AXIS_OFFSET,
- border_rect.mRight, center_y - axis_offset_rb);
- mBtnRotateRight = new LLButton("button_rotate_right", rect,
- "track_control_rotate_right_side.png",
- "track_control_rotate_right_side_active.png",
- "", onRotateRightClick, this);
- mBtnRotateRight->setHeldDownCallback(onRotateRightClickNoSound);
- addChild(mBtnRotateRight);
- mLabelW = new LLTextBox("labelW", rect, TRACKBALL_LABEL_W);
- addChild(mLabelW);
- rect = LLRect(center_x - TRACKBALL_AXIS_OFFSET,
- border_rect.mBottom + TRACKBALL_BTN_SIZE,
- center_x + axis_offset_rb, border_rect.mBottom);
- mBtnRotateBottom = new LLButton("button_rotate_bottom", rect,
- "track_control_rotate_bottom.png",
- "track_control_rotate_bottom_active.png",
- "", onRotateBottomClick, this);
- mBtnRotateBottom->setHeldDownCallback(onRotateBottomClickNoSound);
- addChild(mBtnRotateBottom);
- mLabelS = new LLTextBox("labelS", rect, TRACKBALL_LABEL_S);
- addChild(mLabelS);
- rect = LLRect(border_rect.mLeft, center_y + TRACKBALL_AXIS_OFFSET,
- border_rect.mLeft + TRACKBALL_BTN_SIZE,
- center_y - axis_offset_rb);
- mBtnRotateLeft = new LLButton("button_rotate_left", rect,
- "track_control_rotate_left_side.png",
- "track_control_rotate_left_side_active.png",
- "", onRotateLeftClick, this);
- mBtnRotateLeft->setHeldDownCallback(onRotateLeftClickNoSound);
- addChild(mBtnRotateLeft);
- rect.translate(TRACKBALL_BTN_SIZE / 2, 0);
- mLabelE = new LLTextBox("labelE", rect, TRACKBALL_LABEL_E);
- addChild(mLabelE);
- S32 half_width = mImgSphere->getWidth() / 2;
- S32 half_height = mImgSphere->getHeight() / 2;
- rect = LLRect(center_x - half_width, center_y + half_height,
- center_x + half_width, center_y - half_height);
- mTouchArea = new LLPanel("touch area", rect);
- addChild(mTouchArea);
- }
- bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const
- {
- S32 x1 = x - mTouchArea->getRect().getCenterX();
- S32 y1 = y - mTouchArea->getRect().getCenterY();
- S32 radius = mTouchArea->getRect().getWidth() / 2;
- return x1 * x1 + y1 * y1 <= radius * radius;
- }
- void LLVirtualTrackball::drawThumb(S32 x, S32 y, EThumbMode mode,
- bool upper_hemi)
- {
- LLUIImage* thumb;
- if (mode == EThumbMode::SUN)
- {
- thumb = upper_hemi ? mImgSunFront : mImgSunBack;
- }
- else
- {
- thumb = upper_hemi ? mImgMoonFront : mImgMoonBack;
- }
- S32 half_width = thumb->getWidth() / 2;
- S32 half_height = thumb->getHeight() / 2;
- thumb->draw(LLRect(x - half_width, y + half_height, x + half_width,
- y - half_height));
- }
- //virtual
- void LLVirtualTrackball::draw()
- {
- const LLRect& rect = mTouchArea->getRect();
- S32 half_width = rect.getWidth() / 2;
- S32 half_height = rect.getHeight() / 2;
- LLVector3 draw_point = LLVector3::x_axis * mValue;
- draw_point.mV[VX] = (draw_point.mV[VX] + 1.f) * half_width + rect.mLeft;
- draw_point.mV[VY] = (draw_point.mV[VY] + 1.f) * half_height + rect.mBottom;
- bool upper = draw_point.mV[VZ] >= 0.f;
- mImgSphere->draw(rect, upper ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
- drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper);
- if (sDebugRects)
- {
- gGL.color4fv(LLColor4::red.mV);
- gl_circle_2d(rect.getCenterX(), rect.getCenterY(),
- mImgSphere->getWidth() / 2, 60, false);
- gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY],
- mImgSunFront->getWidth() / 2, 12, false);
- }
- bool enabled = isInEnabledChain();
- mLabelN->setVisible(enabled);
- mLabelS->setVisible(enabled);
- mLabelW->setVisible(enabled);
- mLabelE->setVisible(enabled);
- mBtnRotateTop->setVisible(enabled);
- mBtnRotateBottom->setVisible(enabled);
- mBtnRotateLeft->setVisible(enabled);
- mBtnRotateRight->setVisible(enabled);
- LLView::draw();
- }
- //virtual
- bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask)
- {
- switch (key)
- {
- case KEY_DOWN:
- onRotateTopClick(this);
- break;
- case KEY_LEFT:
- onRotateRightClick(this);
- break;
- case KEY_UP:
- onRotateBottomClick(this);
- break;
- case KEY_RIGHT:
- onRotateLeftClick(this);
- break;
- default:
- return false;
- }
- return true;
- }
- //virtual
- bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask)
- {
- if (hasMouseCapture())
- {
- if (mDragMode == DRAG_SCROLL)
- {
- // Trackball (move to roll) mode
- LLQuaternion delta;
- F32 rot_x = x - mPrevX;
- F32 rot_y = y - mPrevY;
- F32 abs_rot_x = fabsf(rot_x);
- if (abs_rot_x > 1.f)
- {
- // Changing X: rotate around Y axis
- delta.setAngleAxis(mIncrementMouse * abs_rot_x, 0.f,
- rot_x < 0.f ? -1.f : 1.f, 0.f);
- mValue *= delta;
- }
- F32 abs_rot_y = fabsf(rot_y);
- if (abs_rot_y > 1.f)
- {
- // Changing Y: rotate around X axis
- delta.setAngleAxis(mIncrementMouse * abs_rot_y,
- rot_y < 0.f ? 1.f : -1.f, 0.f, 0.f);
- mValue *= delta;
- }
- }
- else
- {
- // Set on click mode
- if (!pointInTouchCircle(x, y))
- {
- // Do not drag outside the circle
- return true;
- }
- const LLRect& rect = mTouchArea->getRect();
- F32 radius = rect.getWidth() / 2;
- F32 xx = x - rect.getCenterX();
- F32 yy = y - rect.getCenterY();
- F32 dist = sqrtf(xx * xx + yy * yy);
- F32 altitude = llclamp(acosf(dist / radius), 0.f, F_PI_BY_TWO);
- F32 azimuth = llclamp(acosf(xx / dist), 0.f, F_PI);
- if (yy < 0.f)
- {
- azimuth = F_TWO_PI - azimuth;
- }
- LLVector3 draw_point = LLVector3::x_axis * mValue;
- if (draw_point.mV[VZ] >= 0.f)
- {
- if (is_approx_zero(altitude)) // do not change the hemisphere
- {
- altitude = -F_APPROXIMATELY_ZERO;
- }
- else
- {
- altitude = -altitude;
- }
- }
- mValue.setAngleAxis(altitude, 0.f, 1.f, 0.f);
- LLQuaternion az_quat;
- az_quat.setAngleAxis(azimuth, 0.f, 0.f, 1.f);
- mValue *= az_quat;
- }
- mValue.normalize();
- mPrevX = x;
- mPrevY = y;
- onCommit();
- }
- return true;
- }
- //virtual
- bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask)
- {
- if (hasMouseCapture())
- {
- mPrevX = mPrevY = 0;
- gFocusMgr.setMouseCapture(NULL);
- make_ui_sound("UISndClickRelease");
- }
- return LLView::handleMouseUp(x, y, mask);
- }
- //virtual
- bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask)
- {
- if (pointInTouchCircle(x, y))
- {
- mPrevX = x;
- mPrevY = y;
- gFocusMgr.setMouseCapture(this);
- mDragMode = mask == MASK_CONTROL ? DRAG_SCROLL : DRAG_SET;
- make_ui_sound("UISndClick");
- }
- return LLView::handleMouseDown(x, y, mask);
- }
- //virtual
- LLSD LLVirtualTrackball::getValue() const
- {
- return mValue.getValue();
- }
- void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w)
- {
- mValue.set(x, y, z, w);
- }
- //virtual
- void LLVirtualTrackball::setValue(const LLSD& value)
- {
- if (value.isArray() && value.size() == 4)
- {
- mValue.setValue(value);
- }
- }
- void LLVirtualTrackball::setValueAndCommit(const LLQuaternion& value)
- {
- mValue = value;
- onCommit();
- }
- void LLVirtualTrackball::getAzimuthAndElevationDeg(F32& azim, F32& elev)
- {
- mValue.getAzimuthAndElevation(azim, elev);
- azim *= RAD_TO_DEG;
- elev *= RAD_TO_DEG;
- }
- //static
- void LLVirtualTrackball::onRotateTopClick(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 1.f, 0.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- make_ui_sound("UISndClick");
- }
- }
- //static
- void LLVirtualTrackball::onRotateBottomClick(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, -1.f, 0.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- make_ui_sound("UISndClick");
- }
- }
- //static
- void LLVirtualTrackball::onRotateLeftClick(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 0.f, 1.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- make_ui_sound("UISndClick");
- }
- }
- //static
- void LLVirtualTrackball::onRotateRightClick(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 0.f, -1.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- make_ui_sound("UISndClick");
- }
- }
- //static
- void LLVirtualTrackball::onRotateTopClickNoSound(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 1.f, 0.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- }
- }
- //static
- void LLVirtualTrackball::onRotateBottomClickNoSound(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, -1.f, 0.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- }
- }
- //static
- void LLVirtualTrackball::onRotateLeftClickNoSound(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 0.f, 1.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- }
- }
- //static
- void LLVirtualTrackball::onRotateRightClickNoSound(void* userdata)
- {
- LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
- if (self && self->getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(self->mIncrementBtn, 0.f, -1.f, 0.f);
- self->setValueAndCommit(self->mValue * delta);
- }
- }
- //virtual
- const std::string& LLVirtualTrackball::getTag() const
- {
- return LL_SUN_MOON_TRACKBALL_TAG;
- }
- //virtual
- LLXMLNodePtr LLVirtualTrackball::getXML(bool save_children) const
- {
- LLXMLNodePtr node = LLUICtrl::getXML();
- node->setName(LL_SUN_MOON_TRACKBALL_TAG);
- node->createChild("increment_angle_mouse",
- true)->setIntValue(mIncrementMouse);
- node->createChild("increment_angle_btn", true)->setIntValue(mIncrementBtn);
- node->createChild("thumb_mode",
- true)->setStringValue(mThumbMode == MOON ? "moon"
- : "sun");
- return node;
- }
- //static
- LLView* LLVirtualTrackball::fromXML(LLXMLNodePtr node, LLView* parent,
- LLUICtrlFactory* factory)
- {
- std::string name = LL_SUN_MOON_TRACKBALL_TAG;
- node->getAttributeString("name", name);
- LLRect rect;
- createRect(node, rect, parent,
- LLRect(0, TRACKBALL_HEIGHT, TRACKBALL_WIDTH, 0));
- LLUICtrlCallback callback = NULL;
- LLVirtualTrackball* trackball = new LLVirtualTrackball(name, rect,
- callback, NULL);
- F32 increment_angle_mouse = 0.5f;
- node->getAttributeF32("increment_angle_mouse", increment_angle_mouse);
- trackball->mIncrementMouse = DEG_TO_RAD * increment_angle_mouse;
- F32 increment_angle_btn = 3.f;
- node->getAttributeF32("increment_angle_btn", increment_angle_btn);
- trackball->mIncrementBtn = DEG_TO_RAD * increment_angle_btn;
- std::string thumb_mode;
- node->getAttributeString("thumb_mode", thumb_mode);
- trackball->mThumbMode = thumb_mode == "moon" ? MOON : SUN;
- trackball->initFromXML(node, parent);
- return trackball;
- }
|