summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt4
-rw-r--r--indra/llui/llfolderview.cpp25
-rw-r--r--indra/llui/llfolderview.h8
-rw-r--r--indra/llui/llmultislider.cpp325
-rw-r--r--indra/llui/llmultislider.h21
-rw-r--r--indra/llui/llmultisliderctrl.cpp24
-rw-r--r--indra/llui/llmultisliderctrl.h21
-rw-r--r--indra/llui/llslider.cpp7
-rw-r--r--indra/llui/llslider.h4
-rw-r--r--indra/llui/llui.h5
-rw-r--r--indra/llui/llvirtualtrackball.cpp480
-rw-r--r--indra/llui/llvirtualtrackball.h160
-rw-r--r--indra/llui/llxyvector.cpp317
-rw-r--r--indra/llui/llxyvector.h118
14 files changed, 1422 insertions, 97 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 8054eb3619..54e4d296ab 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -133,8 +133,10 @@ set(llui_SOURCE_FILES
llview.cpp
llviewquery.cpp
llviewereventrecorder.cpp
+ llvirtualtrackball.cpp
llwindowshade.cpp
llxuiparser.cpp
+ llxyvector.cpp
)
set(llui_HEADER_FILES
@@ -249,8 +251,10 @@ set(llui_HEADER_FILES
llview.h
llviewereventrecorder.h
llviewquery.h
+ llvirtualtrackball.h
llwindowshade.h
llxuiparser.h
+ llxyvector.h
)
set_source_files_properties(${llui_HEADER_FILES}
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index 895753aeae..9d0f4b6db1 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -149,6 +149,7 @@ LLFolderView::Params::Params()
use_label_suffix("use_label_suffix"),
allow_multiselect("allow_multiselect", true),
show_empty_message("show_empty_message", true),
+ suppress_folder_menu("suppress_folder_menu", false),
use_ellipses("use_ellipses", false),
options_menu("options_menu", "")
{
@@ -167,6 +168,7 @@ LLFolderView::LLFolderView(const Params& p)
mRenameItem( NULL ),
mNeedsScroll( FALSE ),
mUseLabelSuffix(p.use_label_suffix),
+ mSuppressFolderMenu(p.suppress_folder_menu),
mPinningSelectedItem(FALSE),
mNeedsAutoSelect( FALSE ),
mAutoSelectOverride(FALSE),
@@ -1432,10 +1434,13 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
S32 count = mSelectedItems.size();
+
LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
- if ( handled
+ bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected();
+ if ((handled
&& ( count > 0 && (hasVisibleChildren()) ) // show menu only if selected items are visible
- && menu )
+ && menu ) &&
+ !hide_folder_menu)
{
if (mCallbackRegistrar)
{
@@ -1449,7 +1454,7 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
if (mCallbackRegistrar)
{
mCallbackRegistrar->popScope();
- }
+ }
}
else
{
@@ -1862,6 +1867,20 @@ void LLFolderView::updateMenu()
}
}
+bool LLFolderView::isFolderSelected()
+{
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(*item_iter);
+ if (folder != NULL)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
bool LLFolderView::selectFirstItem()
{
for (folders_t::iterator iter = mFolders.begin();
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 2926e160d0..69824992c1 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -92,7 +92,8 @@ public:
allow_multiselect,
show_empty_message,
use_ellipses,
- show_item_link_overlays;
+ show_item_link_overlays,
+ suppress_folder_menu;
Mandatory<LLFolderViewModelInterface*> view_model;
Optional<LLFolderViewGroupedItemModel*> grouped_item_model;
Mandatory<std::string> options_menu;
@@ -259,6 +260,8 @@ protected:
void closeRenamer( void );
+ bool isFolderSelected();
+
bool selectFirstItem();
bool selectLastItem();
@@ -282,7 +285,8 @@ protected:
mDragAndDropThisFrame,
mShowItemLinkOverlays,
mShowSelectionContext,
- mShowSingleSelection;
+ mShowSingleSelection,
+ mSuppressFolderMenu;
// Renaming variables and methods
LLFolderViewItem* mRenameItem; // The item currently being renamed
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp
index 0aa3e17075..ece6edd285 100644
--- a/indra/llui/llmultislider.cpp
+++ b/indra/llui/llmultislider.cpp
@@ -54,13 +54,18 @@ LLMultiSlider::SliderParams::SliderParams()
LLMultiSlider::Params::Params()
: max_sliders("max_sliders", 1),
allow_overlap("allow_overlap", false),
+ loop_overlap("loop_overlap", false),
+ orientation("orientation"),
+ overlap_threshold("overlap_threshold", 0),
draw_track("draw_track", true),
use_triangle("use_triangle", false),
track_color("track_color"),
thumb_disabled_color("thumb_disabled_color"),
+ thumb_highlight_color("thumb_highlight_color"),
thumb_outline_color("thumb_outline_color"),
thumb_center_color("thumb_center_color"),
thumb_center_selected_color("thumb_center_selected_color"),
+ thumb_image("thumb_image"),
triangle_color("triangle_color"),
mouse_down_callback("mouse_down_callback"),
mouse_up_callback("mouse_up_callback"),
@@ -71,9 +76,9 @@ LLMultiSlider::Params::Params()
LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
: LLF32UICtrl(p),
mMouseOffset( 0 ),
- mDragStartThumbRect( 0, getRect().getHeight(), p.thumb_width, 0 ),
mMaxNumSliders(p.max_sliders),
mAllowOverlap(p.allow_overlap),
+ mLoopOverlap(p.loop_overlap),
mDrawTrack(p.draw_track),
mUseTriangle(p.use_triangle),
mTrackColor(p.track_color()),
@@ -83,12 +88,22 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
mDisabledThumbColor(p.thumb_disabled_color()),
mTriangleColor(p.triangle_color()),
mThumbWidth(p.thumb_width),
+ mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
mMouseDownSignal(NULL),
mMouseUpSignal(NULL)
{
mValue.emptyMap();
mCurSlider = LLStringUtil::null;
-
+
+ if (mOrientation == HORIZONTAL)
+ {
+ mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0);
+ }
+ else
+ {
+ mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0);
+ }
+
if (p.mouse_down_callback.isProvided())
{
setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
@@ -98,6 +113,15 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
}
+ if (p.overlap_threshold.isProvided())
+ {
+ mOverlapThreshold = p.overlap_threshold;
+ }
+ else
+ {
+ mOverlapThreshold = 0;
+ }
+
for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
it != p.sliders.end();
++it)
@@ -111,6 +135,12 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
addSlider(it->value);
}
}
+
+ if (p.thumb_image.isProvided())
+ {
+ mThumbImagep = LLUI::getUIImage(p.thumb_image());
+ }
+ mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast<LLUIColor>(gFocusMgr.getFocusColor());
}
LLMultiSlider::~LLMultiSlider()
@@ -143,14 +173,40 @@ void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from
// look at the current spot
// and see if anything is there
LLSD::map_iterator mIt = mValue.beginMap();
- for(;mIt != mValue.endMap(); mIt++) {
-
- F32 testVal = (F32)mIt->second.asReal() - newValue;
- if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD &&
- mIt->first != name) {
+
+ // increment is our distance between points, use to eliminate round error
+ F32 threshold = mOverlapThreshold + (mIncrement / 4);
+ // If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower)
+ F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f;
+ // If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper)
+ F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f;
+
+ for(;mIt != mValue.endMap(); mIt++)
+ {
+ F32 locationVal = (F32)mIt->second.asReal();
+ // Check nearby values
+ F32 testVal = locationVal - newValue;
+ if (testVal > -threshold
+ && testVal < threshold
+ && mIt->first != name)
+ {
hit = true;
break;
}
+ if (mLoopOverlap)
+ {
+ // Check edge overlap values
+ if (locationVal < loop_up_check)
+ {
+ hit = true;
+ break;
+ }
+ if (locationVal > loop_down_check)
+ {
+ hit = true;
+ break;
+ }
+ }
}
// if none found, stop
@@ -170,13 +226,26 @@ void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from
}
F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
+ if (mOrientation == HORIZONTAL)
+ {
+ S32 left_edge = mThumbWidth/2;
+ S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
- S32 left_edge = mThumbWidth/2;
- S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
+ S32 x = left_edge + S32( t * (right_edge - left_edge) );
- S32 x = left_edge + S32( t * (right_edge - left_edge) );
- mThumbRects[name].mLeft = x - (mThumbWidth/2);
- mThumbRects[name].mRight = x + (mThumbWidth/2);
+ mThumbRects[name].mLeft = x - (mThumbWidth / 2);
+ mThumbRects[name].mRight = x + (mThumbWidth / 2);
+ }
+ else
+ {
+ S32 bottom_edge = mThumbWidth/2;
+ S32 top_edge = getRect().getHeight() - (mThumbWidth/2);
+
+ S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) );
+
+ mThumbRects[name].mTop = x + (mThumbWidth / 2);
+ mThumbRects[name].mBottom = x - (mThumbWidth / 2);
+ }
}
void LLMultiSlider::setValue(const LLSD& value)
@@ -196,7 +265,11 @@ void LLMultiSlider::setValue(const LLSD& value)
F32 LLMultiSlider::getSliderValue(const std::string& name) const
{
- return (F32)mValue[name].asReal();
+ if (mValue.has(name))
+ {
+ return (F32)mValue[name].asReal();
+ }
+ return 0;
}
void LLMultiSlider::setCurSlider(const std::string& name)
@@ -206,6 +279,38 @@ void LLMultiSlider::setCurSlider(const std::string& name)
}
}
+F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const
+{
+ F32 t = 0;
+ if (mOrientation == HORIZONTAL)
+ {
+ S32 left_edge = mThumbWidth / 2;
+ S32 right_edge = getRect().getWidth() - (mThumbWidth / 2);
+
+ xpos += mMouseOffset;
+ xpos = llclamp(xpos, left_edge, right_edge);
+
+ t = F32(xpos - left_edge) / (right_edge - left_edge);
+ }
+ else
+ {
+ S32 bottom_edge = mThumbWidth / 2;
+ S32 top_edge = getRect().getHeight() - (mThumbWidth / 2);
+
+ ypos += mMouseOffset;
+ ypos = llclamp(ypos, bottom_edge, top_edge);
+
+ t = F32(ypos - bottom_edge) / (top_edge - bottom_edge);
+ }
+
+ return((t * (mMaxValue - mMinValue)) + mMinValue);
+}
+
+void LLMultiSlider::resetCurSlider()
+{
+ mCurSlider = LLStringUtil::null;
+}
+
const std::string& LLMultiSlider::addSlider()
{
return addSlider(mInitialValue);
@@ -230,7 +335,14 @@ const std::string& LLMultiSlider::addSlider(F32 val)
}
// add a new thumb rect
- mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
+ if (mOrientation == HORIZONTAL)
+ {
+ mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
+ }
+ else
+ {
+ mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
+ }
// add the value and set the current slider to this one
mValue.insert(newName.str(), initVal);
@@ -256,7 +368,14 @@ void LLMultiSlider::addSlider(F32 val, const std::string& name)
}
// add a new thumb rect
- mThumbRects[name] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
+ if (mOrientation == HORIZONTAL)
+ {
+ mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
+ }
+ else
+ {
+ mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
+ }
// add the value and set the current slider to this one
mValue.insert(name, initVal);
@@ -278,11 +397,13 @@ bool LLMultiSlider::findUnusedValue(F32& initVal)
// look at the current spot
// and see if anything is there
+ F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4);
LLSD::map_iterator mIt = mValue.beginMap();
for(;mIt != mValue.endMap(); mIt++) {
F32 testVal = (F32)mIt->second.asReal() - initVal;
- if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD) {
+ if(testVal > -threshold && testVal < threshold)
+ {
hit = true;
break;
}
@@ -334,10 +455,15 @@ void LLMultiSlider::deleteSlider(const std::string& name)
void LLMultiSlider::clear()
{
- while(mThumbRects.size() > 0) {
+ while(mThumbRects.size() > 0 && mValue.size() > 0) {
deleteCurSlider();
}
+ if (mThumbRects.size() > 0 || mValue.size() > 0)
+ {
+ LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL;
+ }
+
LLF32UICtrl::clear();
}
@@ -345,14 +471,15 @@ BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
{
if( gFocusMgr.getMouseCapture() == this )
{
- S32 left_edge = mThumbWidth/2;
- S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
-
- x += mMouseOffset;
- x = llclamp( x, left_edge, right_edge );
-
- F32 t = F32(x - left_edge) / (right_edge - left_edge);
- setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
+// S32 left_edge = mThumbWidth/2;
+// S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
+//
+// x += mMouseOffset;
+// x = llclamp( x, left_edge, right_edge );
+//
+// F32 t = F32(x - left_edge) / (right_edge - left_edge);
+// setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
+ setCurSliderValue(getSliderValueFromPos(x, y));
onCommit();
getWindow()->setCursor(UI_CURSOR_ARROW);
@@ -416,20 +543,30 @@ BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
}
}
- // Find the offset of the actual mouse location from the center of the thumb.
- if (mThumbRects[mCurSlider].pointInRect(x,y))
- {
- mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth/2) - x;
- }
- else
+ if (!mCurSlider.empty())
{
- mMouseOffset = 0;
- }
+ // Find the offset of the actual mouse location from the center of the thumb.
+ if (mThumbRects[mCurSlider].pointInRect(x,y))
+ {
+ if (mOrientation == HORIZONTAL)
+ {
+ mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x;
+ }
+ else
+ {
+ mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y;
+ }
+ }
+ else
+ {
+ mMouseOffset = 0;
+ }
- // Start dragging the thumb
- // No handler needed for focus lost since this class has no state that depends on it.
- gFocusMgr.setMouseCapture( this );
- mDragStartThumbRect = mThumbRects[mCurSlider];
+ // Start dragging the thumb
+ // No handler needed for focus lost since this class has no state that depends on it.
+ gFocusMgr.setMouseCapture( this );
+ mDragStartThumbRect = mThumbRects[mCurSlider];
+ }
}
make_ui_sound("UISndClick");
@@ -483,9 +620,18 @@ void LLMultiSlider::draw()
// Track
LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square");
- static LLUICachedControl<S32> multi_track_height ("UIMultiTrackHeight", 0);
- S32 height_offset = (getRect().getHeight() - multi_track_height) / 2;
- LLRect track_rect(0, getRect().getHeight() - height_offset, getRect().getWidth(), height_offset );
+ static LLUICachedControl<S32> multi_track_height_width ("UIMultiTrackHeight", 0);
+ S32 height_offset = 0;
+ S32 width_offset = 0;
+ if (mOrientation == HORIZONTAL)
+ {
+ height_offset = (getRect().getHeight() - multi_track_height_width) / 2;
+ }
+ else
+ {
+ width_offset = (getRect().getWidth() - multi_track_height_width) / 2;
+ }
+ LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset);
if(mDrawTrack)
@@ -510,7 +656,7 @@ void LLMultiSlider::draw()
mTriangleColor.get() % opacity, TRUE);
}
}
- else if (!thumb_imagep)
+ else if (!thumb_imagep && !mThumbImagep)
{
// draw all the thumbs
curSldrIt = mThumbRects.end();
@@ -540,15 +686,33 @@ void LLMultiSlider::draw()
gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE);
}
}
- else if( gFocusMgr.getMouseCapture() == this )
+ else
{
- // draw drag start
- thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ LLMouseHandler* capture = gFocusMgr.getMouseCapture();
+ if (capture == this)
+ {
+ // draw drag start (ghost)
+ if (mThumbImagep)
+ {
+ mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ }
+ else
+ {
+ thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ }
+ }
// draw the highlight
- if (hasFocus())
+ if (hasFocus() && !mCurSlider.empty())
{
- thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
}
// draw the thumbs
@@ -565,44 +729,49 @@ void LLMultiSlider::draw()
}
// the draw command
- thumb_imagep->drawSolid(mIt->second, curThumbColor);
+ if (mThumbImagep)
+ {
+ if (getEnabled())
+ {
+ mThumbImagep->draw(mIt->second);
+ }
+ else
+ {
+ mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f);
+ }
+ }
+ else if (capture == this)
+ {
+ thumb_imagep->drawSolid(mIt->second, curThumbColor);
+ }
+ else
+ {
+ thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
+ }
}
// draw cur slider last
if(curSldrIt != mThumbRects.end())
{
- thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
- }
-
- }
- else
- {
- // draw highlight
- if (hasFocus())
- {
- thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
- }
-
- // draw thumbs
- curSldrIt = mThumbRects.end();
- for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
- {
-
- // choose the color
- curThumbColor = mThumbCenterColor.get();
- if(mIt->first == mCurSlider)
+ if (mThumbImagep)
{
- curSldrIt = mIt;
- continue;
- //curThumbColor = mThumbCenterSelectedColor;
- }
-
- thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
- }
-
- if(curSldrIt != mThumbRects.end())
- {
- thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
+ if (getEnabled())
+ {
+ mThumbImagep->draw(curSldrIt->second);
+ }
+ else
+ {
+ mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f);
+ }
+ }
+ else if (capture == this)
+ {
+ thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
+ }
+ else
+ {
+ thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
+ }
}
}
diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h
index 2b422e89c9..52c6d1ddef 100644
--- a/indra/llui/llmultislider.h
+++ b/indra/llui/llmultislider.h
@@ -47,16 +47,23 @@ public:
Optional<S32> max_sliders;
Optional<bool> allow_overlap,
+ loop_overlap,
draw_track,
use_triangle;
+ Optional<F32> overlap_threshold;
+
Optional<LLUIColor> track_color,
thumb_disabled_color,
+ thumb_highlight_color,
thumb_outline_color,
thumb_center_color,
thumb_center_selected_color,
triangle_color;
+ Optional<std::string> orientation,
+ thumb_image;
+
Optional<CommitCallbackParam> mouse_down_callback,
mouse_up_callback;
Optional<S32> thumb_width;
@@ -72,10 +79,12 @@ public:
virtual ~LLMultiSlider();
void setSliderValue(const std::string& name, F32 value, BOOL from_event = FALSE);
F32 getSliderValue(const std::string& name) const;
+ F32 getSliderValueFromPos(S32 xpos, S32 ypos) const;
const std::string& getCurSlider() const { return mCurSlider; }
F32 getCurSliderValue() const { return getSliderValue(mCurSlider); }
void setCurSlider(const std::string& name);
+ void resetCurSlider();
void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mCurSlider, val, from_event); }
/*virtual*/ void setValue(const LLSD& value);
@@ -98,6 +107,10 @@ public:
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ void draw();
+ S32 getMaxNumSliders() { return mMaxNumSliders; }
+ S32 getCurNumSliders() { return mValue.size(); }
+ bool canAddSliders() { return mValue.size() < mMaxNumSliders; }
+
protected:
LLSD mValue;
std::string mCurSlider;
@@ -105,6 +118,8 @@ protected:
S32 mMaxNumSliders;
BOOL mAllowOverlap;
+ BOOL mLoopOverlap;
+ F32 mOverlapThreshold;
BOOL mDrawTrack;
BOOL mUseTriangle; /// hacked in toggle to use a triangle
@@ -116,11 +131,15 @@ protected:
mThumbRects;
LLUIColor mTrackColor;
LLUIColor mThumbOutlineColor;
+ LLUIColor mThumbHighlightColor;
LLUIColor mThumbCenterColor;
LLUIColor mThumbCenterSelectedColor;
LLUIColor mDisabledThumbColor;
LLUIColor mTriangleColor;
-
+ LLUIImagePtr mThumbImagep; //blimps on the slider, for now no 'disabled' support
+
+ const EOrientation mOrientation;
+
commit_signal_t* mMouseDownSignal;
commit_signal_t* mMouseUpSignal;
};
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index c460a08afc..972567ef9b 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -53,6 +53,12 @@ LLMultiSliderCtrl::Params::Params()
can_edit_text("can_edit_text", false),
max_sliders("max_sliders", 1),
allow_overlap("allow_overlap", false),
+ loop_overlap("loop_overlap", false),
+ orientation("orientation"),
+ thumb_image("thumb_image"),
+ thumb_width("thumb_width"),
+ thumb_highlight_color("thumb_highlight_color"),
+ overlap_threshold("overlap_threshold", 0),
draw_track("draw_track", true),
use_triangle("use_triangle", false),
decimal_digits("decimal_digits", 3),
@@ -167,6 +173,19 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
params.increment(p.increment);
params.max_sliders(p.max_sliders);
params.allow_overlap(p.allow_overlap);
+ params.loop_overlap(p.loop_overlap);
+ if (p.overlap_threshold.isProvided())
+ {
+ params.overlap_threshold = p.overlap_threshold;
+ }
+ params.orientation(p.orientation);
+ params.thumb_image(p.thumb_image);
+ params.thumb_highlight_color(p.thumb_highlight_color);
+ if (p.thumb_width.isProvided())
+ {
+ // otherwise should be provided by template
+ params.thumb_width(p.thumb_width);
+ }
params.draw_track(p.draw_track);
params.use_triangle(p.use_triangle);
params.control_name(p.control_name);
@@ -213,6 +232,11 @@ void LLMultiSliderCtrl::setCurSlider(const std::string& name)
mCurValue = mMultiSlider->getCurSliderValue();
}
+void LLMultiSliderCtrl::resetCurSlider()
+{
+ mMultiSlider->resetCurSlider();
+}
+
BOOL LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
{
BOOL res = FALSE;
diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h
index b6a3542376..c370ebf505 100644
--- a/indra/llui/llmultisliderctrl.h
+++ b/indra/llui/llmultisliderctrl.h
@@ -51,14 +51,22 @@ public:
text_width;
Optional<bool> show_text,
can_edit_text;
- Optional<S32> decimal_digits;
+ Optional<S32> decimal_digits,
+ thumb_width;
Optional<S32> max_sliders;
Optional<bool> allow_overlap,
+ loop_overlap,
draw_track,
use_triangle;
+ Optional<std::string> orientation,
+ thumb_image;
+
+ Optional<F32> overlap_threshold;
+
Optional<LLUIColor> text_color,
- text_disabled_color;
+ text_disabled_color,
+ thumb_highlight_color;
Optional<CommitCallbackParam> mouse_down_callback,
mouse_up_callback;
@@ -74,7 +82,7 @@ protected:
public:
virtual ~LLMultiSliderCtrl();
- F32 getSliderValue(const std::string& name) const;
+ F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); }
void setSliderValue(const std::string& name, F32 v, BOOL from_event = FALSE);
virtual void setValue(const LLSD& value );
@@ -84,6 +92,7 @@ public:
const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); }
F32 getCurSliderValue() const { return mCurValue; }
void setCurSlider(const std::string& name);
+ void resetCurSlider();
void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); }
virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
@@ -98,6 +107,8 @@ public:
void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);}
void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);}
+ F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); }
+
/// for adding and deleting sliders
const std::string& addSlider();
const std::string& addSlider(F32 val);
@@ -107,6 +118,10 @@ public:
F32 getMinValue() const { return mMultiSlider->getMinValue(); }
F32 getMaxValue() const { return mMultiSlider->getMaxValue(); }
+ S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); }
+ S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); }
+ bool canAddSliders() { return mMultiSlider->canAddSliders(); }
+
void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); }
void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
index ebbb951ee6..62df5a2c38 100644
--- a/indra/llui/llslider.cpp
+++ b/indra/llui/llslider.cpp
@@ -42,7 +42,6 @@ static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar");
LLSlider::Params::Params()
: orientation ("orientation", std::string ("horizontal")),
- track_color("track_color"),
thumb_outline_color("thumb_outline_color"),
thumb_center_color("thumb_center_color"),
thumb_image("thumb_image"),
@@ -60,7 +59,6 @@ LLSlider::LLSlider(const LLSlider::Params& p)
: LLF32UICtrl(p),
mMouseOffset( 0 ),
mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL),
- mTrackColor(p.track_color()),
mThumbOutlineColor(p.thumb_outline_color()),
mThumbCenterColor(p.thumb_center_color()),
mThumbImage(p.thumb_image),
@@ -331,8 +329,9 @@ void LLSlider::draw()
highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom);
}
- trackImage->draw(track_rect, LLColor4::white % alpha);
- trackHighlightImage->draw(highlight_rect, LLColor4::white % alpha);
+ LLColor4 color = isInEnabledChain() ? LLColor4::white % alpha : LLColor4::white % (0.6f * alpha);
+ trackImage->draw(track_rect, color);
+ trackHighlightImage->draw(highlight_rect, color);
// Thumb
if (hasFocus())
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
index 3b492d8182..484a5373b3 100644
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -38,8 +38,7 @@ public:
{
Optional<std::string> orientation;
- Optional<LLUIColor> track_color,
- thumb_outline_color,
+ Optional<LLUIColor> thumb_outline_color,
thumb_center_color;
Optional<LLUIImage*> thumb_image,
@@ -99,7 +98,6 @@ private:
const EOrientation mOrientation;
LLRect mThumbRect;
- LLUIColor mTrackColor;
LLUIColor mThumbOutlineColor;
LLUIColor mThumbCenterColor;
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index d7151dbee9..59fc1ddcef 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -33,7 +33,6 @@
#include "llcontrol.h"
#include "llcoord.h"
#include "llcontrol.h"
-#include "llglslshader.h"
#include "llinitparam.h"
#include "llregistry.h"
#include "llrender2dutils.h"
@@ -49,7 +48,6 @@
// for initparam specialization
#include "llfontgl.h"
-
class LLUUID;
class LLWindow;
class LLView;
@@ -77,7 +75,8 @@ enum EDragAndDropType
DAD_MESH = 15,
DAD_WIDGET = 16,
DAD_PERSON = 17,
- DAD_COUNT = 18, // number of types in this enum
+ DAD_SETTINGS = 18,
+ DAD_COUNT = 19, // number of types in this enum
};
// Reasons for drags to be denied.
diff --git a/indra/llui/llvirtualtrackball.cpp b/indra/llui/llvirtualtrackball.cpp
new file mode 100644
index 0000000000..723643dd25
--- /dev/null
+++ b/indra/llui/llvirtualtrackball.cpp
@@ -0,0 +1,480 @@
+/**
+* @file LLVirtualTrackball.cpp
+* @author Andrey Lihatskiy
+* @brief Implementation for LLVirtualTrackball
+*
+* $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 for positioning the sun and the moon in the celestial sphere.
+
+#include "linden_common.h"
+#include "llvirtualtrackball.h"
+#include "llstring.h"
+#include "llrect.h"
+#include "lluictrlfactory.h"
+#include "llrender.h"
+
+// Globals
+static LLDefaultChildRegistry::Register<LLVirtualTrackball> register_virtual_trackball("sun_moon_trackball");
+
+const LLVector3 VectorZero(1.0f, 0.0f, 0.0f);
+
+LLVirtualTrackball::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"),
+ btn_rotate_top("button_rotate_top"),
+ btn_rotate_bottom("button_rotate_bottom"),
+ btn_rotate_left("button_rotate_left"),
+ btn_rotate_right("button_rotate_right"),
+ thumb_mode("thumb_mode"),
+ lbl_N("labelN"),
+ lbl_S("labelS"),
+ lbl_W("labelW"),
+ lbl_E("labelE"),
+ increment_angle_mouse("increment_angle_mouse", 0.5f),
+ increment_angle_btn("increment_angle_btn", 3.0f)
+{
+}
+
+LLVirtualTrackball::LLVirtualTrackball(const LLVirtualTrackball::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),
+ mThumbMode(p.thumb_mode() == "moon" ? ThumbMode::MOON : ThumbMode::SUN),
+ mIncrementMouse(DEG_TO_RAD * p.increment_angle_mouse()),
+ mIncrementBtn(DEG_TO_RAD * p.increment_angle_btn())
+{
+ LLRect border_rect = getLocalRect();
+ S32 centerX = border_rect.getCenterX();
+ S32 centerY = border_rect.getCenterY();
+ U32 btn_size = 32; // width & height
+ U32 axis_offset_lt = 16; // offset from the axis for left/top sides
+ U32 axis_offset_rb = btn_size - axis_offset_lt; // and for right/bottom
+
+ LLViewBorder::Params border = p.border;
+ border.rect(border_rect);
+ mBorder = LLUICtrlFactory::create<LLViewBorder>(border);
+ addChild(mBorder);
+
+
+ LLButton::Params btn_rt = p.btn_rotate_top;
+ btn_rt.rect(LLRect(centerX - axis_offset_lt, border_rect.mTop, centerX + axis_offset_rb, border_rect.mTop - btn_size));
+ btn_rt.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
+ btn_rt.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
+ btn_rt.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopMouseEnter, this));
+ mBtnRotateTop = LLUICtrlFactory::create<LLButton>(btn_rt);
+ addChild(mBtnRotateTop);
+
+ LLTextBox::Params lbl_N = p.lbl_N;
+ LLRect rect_N = btn_rt.rect;
+ //rect_N.translate(btn_rt.rect().getWidth(), 0);
+ lbl_N.rect = rect_N;
+ lbl_N.initial_value(lbl_N.label());
+ mLabelN = LLUICtrlFactory::create<LLTextBox>(lbl_N);
+ addChild(mLabelN);
+
+
+ LLButton::Params btn_rr = p.btn_rotate_right;
+ btn_rr.rect(LLRect(border_rect.mRight - btn_size, centerY + axis_offset_lt, border_rect.mRight, centerY - axis_offset_rb));
+ btn_rr.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
+ btn_rr.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
+ btn_rr.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightMouseEnter, this));
+ mBtnRotateRight = LLUICtrlFactory::create<LLButton>(btn_rr);
+ addChild(mBtnRotateRight);
+
+ LLTextBox::Params lbl_E = p.lbl_E;
+ LLRect rect_E = btn_rr.rect;
+ //rect_E.translate(0, -1 * btn_rr.rect().getHeight());
+ lbl_E.rect = rect_E;
+ lbl_E.initial_value(lbl_E.label());
+ mLabelE = LLUICtrlFactory::create<LLTextBox>(lbl_E);
+ addChild(mLabelE);
+
+
+ LLButton::Params btn_rb = p.btn_rotate_bottom;
+ btn_rb.rect(LLRect(centerX - axis_offset_lt, border_rect.mBottom + btn_size, centerX + axis_offset_rb, border_rect.mBottom));
+ btn_rb.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
+ btn_rb.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
+ btn_rb.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomMouseEnter, this));
+ mBtnRotateBottom = LLUICtrlFactory::create<LLButton>(btn_rb);
+ addChild(mBtnRotateBottom);
+
+ LLTextBox::Params lbl_S = p.lbl_S;
+ LLRect rect_S = btn_rb.rect;
+ //rect_S.translate(btn_rb.rect().getWidth(), 0);
+ lbl_S.rect = rect_S;
+ lbl_S.initial_value(lbl_S.label());
+ mLabelS = LLUICtrlFactory::create<LLTextBox>(lbl_S);
+ addChild(mLabelS);
+
+
+ LLButton::Params btn_rl = p.btn_rotate_left;
+ btn_rl.rect(LLRect(border_rect.mLeft, centerY + axis_offset_lt, border_rect.mLeft + btn_size, centerY - axis_offset_rb));
+ btn_rl.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
+ btn_rl.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
+ btn_rl.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftMouseEnter, this));
+ mBtnRotateLeft = LLUICtrlFactory::create<LLButton>(btn_rl);
+ addChild(mBtnRotateLeft);
+
+ LLTextBox::Params lbl_W = p.lbl_W;
+ LLRect rect_W = btn_rl.rect;
+ //rect_W.translate(0, -1* btn_rl.rect().getHeight());
+ lbl_W.rect = rect_W;
+ lbl_W.initial_value(lbl_W.label());
+ mLabelW = LLUICtrlFactory::create<LLTextBox>(lbl_W);
+ addChild(mLabelW);
+
+
+ LLPanel::Params touch_area;
+ touch_area.rect = LLRect(centerX - mImgSphere->getWidth() / 2,
+ centerY + mImgSphere->getHeight() / 2,
+ centerX + mImgSphere->getWidth() / 2,
+ centerY - mImgSphere->getHeight() / 2);
+ mTouchArea = LLUICtrlFactory::create<LLPanel>(touch_area);
+ addChild(mTouchArea);
+}
+
+LLVirtualTrackball::~LLVirtualTrackball()
+{
+}
+
+BOOL LLVirtualTrackball::postBuild()
+{
+ return TRUE;
+}
+
+
+void LLVirtualTrackball::drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi)
+{
+ LLUIImage* thumb;
+ if (mode == ThumbMode::SUN)
+ {
+ if (upperHemi)
+ {
+ thumb = mImgSunFront;
+ }
+ else
+ {
+ thumb = mImgSunBack;
+ }
+ }
+ else
+ {
+ if (upperHemi)
+ {
+ thumb = mImgMoonFront;
+ }
+ else
+ {
+ thumb = mImgMoonBack;
+ }
+ }
+ thumb->draw(LLRect(x - thumb->getWidth() / 2,
+ y + thumb->getHeight() / 2,
+ x + thumb->getWidth() / 2,
+ y - thumb->getHeight() / 2));
+}
+
+bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const
+{
+ S32 centerX = mTouchArea->getRect().getCenterX();
+ S32 centerY = mTouchArea->getRect().getCenterY();
+
+ bool in_circle = pow(x - centerX, 2) + pow(y - centerY, 2) <= pow(mTouchArea->getRect().getWidth() / 2, 2);
+ return in_circle;
+}
+
+void LLVirtualTrackball::draw()
+{
+ LLVector3 draw_point = VectorZero * mValue;
+
+ S32 halfwidth = mTouchArea->getRect().getWidth() / 2;
+ S32 halfheight = mTouchArea->getRect().getHeight() / 2;
+ draw_point.mV[VX] = (draw_point.mV[VX] + 1.0) * halfwidth + mTouchArea->getRect().mLeft;
+ draw_point.mV[VY] = (draw_point.mV[VY] + 1.0) * halfheight + mTouchArea->getRect().mBottom;
+ bool upper_hemisphere = (draw_point.mV[VZ] >= 0.f);
+
+ mImgSphere->draw(mTouchArea->getRect(), upper_hemisphere ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
+ drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper_hemisphere);
+
+
+ if (LLView::sDebugRects)
+ {
+ gGL.color4fv(LLColor4::red.mV);
+ gl_circle_2d(mTouchArea->getRect().getCenterX(), mTouchArea->getRect().getCenterY(), mImgSphere->getWidth() / 2, 60, false);
+ gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY], mImgSunFront->getWidth() / 2, 12, false);
+ }
+
+ // hide the direction labels when disabled
+ BOOL enabled = isInEnabledChain();
+ mLabelN->setVisible(enabled);
+ mLabelE->setVisible(enabled);
+ mLabelS->setVisible(enabled);
+ mLabelW->setVisible(enabled);
+
+ LLView::draw();
+}
+
+void LLVirtualTrackball::onRotateTopClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 1, 0, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateBottomClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, -1, 0, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateLeftClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 0, 1, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateRightClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 0, -1, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateTopMouseEnter()
+{
+ mBtnRotateTop->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateBottomMouseEnter()
+{
+ mBtnRotateBottom->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateLeftMouseEnter()
+{
+ mBtnRotateLeft->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateRightMouseEnter()
+{
+ mBtnRotateRight->setHighlight(true);
+}
+
+void LLVirtualTrackball::setValue(const LLSD& value)
+{
+ if (value.isArray() && value.size() == 4)
+ {
+ mValue.setValue(value);
+ }
+}
+
+void LLVirtualTrackball::setRotation(const LLQuaternion &value)
+{
+ mValue = value;
+}
+
+void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w)
+{
+ mValue.set(x, y, z, w);
+}
+
+void LLVirtualTrackball::setValueAndCommit(const LLQuaternion &value)
+{
+ mValue = value;
+ onCommit();
+}
+
+LLSD LLVirtualTrackball::getValue() const
+{
+ return mValue.getValue();
+}
+
+LLQuaternion LLVirtualTrackball::getRotation() const
+{
+ return mValue;
+}
+
+BOOL LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (hasMouseCapture())
+ {
+ if (mDragMode == DRAG_SCROLL)
+ { // trackball (move to roll) mode
+ LLQuaternion delta;
+
+ F32 rotX = x - mPrevX;
+ F32 rotY = y - mPrevY;
+
+ if (abs(rotX) > 1)
+ {
+ F32 direction = (rotX < 0) ? -1 : 1;
+ delta.setAngleAxis(mIncrementMouse * abs(rotX), 0, direction, 0); // changing X - rotate around Y axis
+ mValue *= delta;
+ }
+
+ if (abs(rotY) > 1)
+ {
+ F32 direction = (rotY < 0) ? 1 : -1; // reverse for Y (value increases from bottom to top)
+ delta.setAngleAxis(mIncrementMouse * abs(rotY), direction, 0, 0); // changing Y - rotate around X axis
+ mValue *= delta;
+ }
+ }
+ else
+ { // set on click mode
+ if (!pointInTouchCircle(x, y))
+ {
+ return TRUE; // don't drag outside the circle
+ }
+
+ F32 radius = mTouchArea->getRect().getWidth() / 2;
+ F32 xx = x - mTouchArea->getRect().getCenterX();
+ F32 yy = y - mTouchArea->getRect().getCenterY();
+ F32 dist = sqrt(pow(xx, 2) + pow(yy, 2));
+
+ F32 azimuth = llclamp(acosf(xx / dist), 0.0f, F_PI);
+ F32 altitude = llclamp(acosf(dist / radius), 0.0f, F_PI_BY_TWO);
+
+ if (yy < 0)
+ {
+ azimuth = F_TWO_PI - azimuth;
+ }
+
+ LLVector3 draw_point = VectorZero * mValue;
+ if (draw_point.mV[VZ] >= 0.f)
+ {
+ if (is_approx_zero(altitude)) // don't change the hemisphere
+ {
+ altitude = F_APPROXIMATELY_ZERO;
+ }
+ altitude *= -1;
+ }
+
+ mValue.setAngleAxis(altitude, 0, 1, 0);
+ LLQuaternion az_quat;
+ az_quat.setAngleAxis(azimuth, 0, 0, 1);
+ mValue *= az_quat;
+ }
+
+ mPrevX = x;
+ mPrevY = y;
+ onCommit();
+ }
+ return TRUE;
+}
+
+BOOL LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (hasMouseCapture())
+ {
+ mPrevX = 0;
+ mPrevY = 0;
+ gFocusMgr.setMouseCapture(NULL);
+ make_ui_sound("UISndClickRelease");
+ }
+ return LLView::handleMouseUp(x, y, mask);
+}
+
+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);
+}
+
+BOOL LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (pointInTouchCircle(x, y))
+ {
+
+ //make_ui_sound("UISndClick");
+ }
+ return LLView::handleRightMouseDown(x, y, mask);
+}
+
+BOOL LLVirtualTrackball::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_DOWN:
+ onRotateTopClick();
+ handled = TRUE;
+ break;
+ case KEY_LEFT:
+ onRotateRightClick();
+ handled = TRUE;
+ break;
+ case KEY_UP:
+ onRotateBottomClick();
+ handled = TRUE;
+ break;
+ case KEY_RIGHT:
+ onRotateLeftClick();
+ handled = TRUE;
+ break;
+ default:
+ break;
+ }
+ return handled;
+}
+
diff --git a/indra/llui/llvirtualtrackball.h b/indra/llui/llvirtualtrackball.h
new file mode 100644
index 0000000000..2d4b1ece17
--- /dev/null
+++ b/indra/llui/llvirtualtrackball.h
@@ -0,0 +1,160 @@
+/**
+* @file virtualtrackball.h
+* @author Andrey Lihatskiy
+* @brief Header file for LLVirtualTrackball
+*
+* $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 for positioning the sun and the moon in the celestial sphere.
+
+#ifndef LL_LLVIRTUALTRACKBALL_H
+#define LL_LLVIRTUALTRACKBALL_H
+
+#include "lluictrl.h"
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+
+class LLVirtualTrackball
+ : public LLUICtrl
+{
+public:
+ enum ThumbMode
+ {
+ SUN,
+ MOON
+ };
+ enum DragMode
+ {
+ DRAG_SET,
+ DRAG_SCROLL
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLViewBorder::Params> border;
+ Optional<LLUIImage*> image_moon_back,
+ image_moon_front,
+ image_sphere,
+ image_sun_back,
+ image_sun_front;
+
+ Optional<std::string> thumb_mode;
+ Optional<F32> increment_angle_mouse,
+ increment_angle_btn;
+
+ Optional<LLTextBox::Params> lbl_N,
+ lbl_S,
+ lbl_W,
+ lbl_E;
+
+ Optional<LLButton::Params> btn_rotate_top,
+ btn_rotate_bottom,
+ btn_rotate_left,
+ btn_rotate_right;
+
+ Params();
+ };
+
+
+ virtual ~LLVirtualTrackball();
+ /*virtual*/ BOOL postBuild();
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ virtual void draw();
+
+ virtual void setValue(const LLSD& value);
+ void setValue(F32 x, F32 y, F32 z, F32 w);
+ virtual LLSD getValue() const;
+
+ void setRotation(const LLQuaternion &value);
+ LLQuaternion getRotation() const;
+
+protected:
+ friend class LLUICtrlFactory;
+ LLVirtualTrackball(const Params&);
+ void onEditChange();
+
+protected:
+ LLTextBox* mNLabel;
+ LLTextBox* mELabel;
+ LLTextBox* mSLabel;
+ LLTextBox* mWLabel;
+
+ LLButton* mBtnRotateTop;
+ LLButton* mBtnRotateBottom;
+ LLButton* mBtnRotateLeft;
+ LLButton* mBtnRotateRight;
+
+ LLTextBox* mLabelN;
+ LLTextBox* mLabelS;
+ LLTextBox* mLabelW;
+ LLTextBox* mLabelE;
+
+ LLPanel* mTouchArea;
+ LLViewBorder* mBorder;
+
+private:
+ void setValueAndCommit(const LLQuaternion &value);
+ void drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi = true);
+ bool pointInTouchCircle(S32 x, S32 y) const;
+
+ void onRotateTopClick();
+ void onRotateBottomClick();
+ void onRotateLeftClick();
+ void onRotateRightClick();
+
+ void onRotateTopMouseEnter();
+ void onRotateBottomMouseEnter();
+ void onRotateLeftMouseEnter();
+ void onRotateRightMouseEnter();
+
+ S32 mPrevX;
+ S32 mPrevY;
+
+ LLUIImage* mImgMoonBack;
+ LLUIImage* mImgMoonFront;
+ LLUIImage* mImgSunBack;
+ LLUIImage* mImgSunFront;
+ LLUIImage* mImgBtnRotTop;
+ LLUIImage* mImgBtnRotLeft;
+ LLUIImage* mImgBtnRotRight;
+ LLUIImage* mImgBtnRotBottom;
+ LLUIImage* mImgSphere;
+
+ LLQuaternion mValue;
+ ThumbMode mThumbMode;
+ DragMode mDragMode;
+
+ F32 mIncrementMouse;
+ F32 mIncrementBtn;
+};
+
+#endif
+
diff --git a/indra/llui/llxyvector.cpp b/indra/llui/llxyvector.cpp
new file mode 100644
index 0000000000..9d5823e368
--- /dev/null
+++ b/indra/llui/llxyvector.cpp
@@ -0,0 +1,317 @@
+/**
+* @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"
+
+// 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)
+{
+}
+
+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)
+{
+ 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()
+{
+ 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 = centerX + (mValueX * mTouchArea->getRect().getWidth() / (2 * mMaxValueX));
+ S32 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)
+{
+ x = llclamp(x, mMinValueX, mMaxValueX);
+ y = llclamp(y, mMinValueY, mMaxValueY);
+
+ // Round the values to nearest increments
+ x -= mMinValueX;
+ x += mIncrementX / 2.0001f;
+ x -= fmod(x, mIncrementX);
+ x += mMinValueX;
+
+ y -= mMinValueY;
+ y += mIncrementY / 2.0001f;
+ y -= fmod(y, mIncrementY);
+ y += mMinValueY;
+
+ mValueX = x;
+ mValueY = y;
+
+ 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())
+ {
+ F32 valueX = F32(x - mTouchArea->getRect().getCenterX()) / mTouchArea->getRect().getWidth();
+ F32 valueY = F32(y - mTouchArea->getRect().getCenterY()) / mTouchArea->getRect().getHeight();
+
+ valueX *= 2 * mMaxValueX;
+ valueY *= 2 * mMaxValueY;
+
+ 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);
+ }
+}
+
diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h
new file mode 100644
index 0000000000..4d67db3251
--- /dev/null
+++ b/indra/llui/llxyvector.h
@@ -0,0 +1,118 @@
+/**
+* @file llxyvector.h
+* @author Andrey Lihatskiy
+* @brief Header file 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.
+
+#ifndef LL_LLXYVECTOR_H
+#define LL_LLXYVECTOR_H
+
+#include "lluictrl.h"
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "lllineeditor.h"
+
+class LLXYVector
+ : public LLUICtrl
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLLineEditor::Params> x_entry;
+ Optional<LLLineEditor::Params> y_entry;
+ Optional<LLPanel::Params> touch_area;
+ Optional<LLViewBorder::Params> border;
+ Optional<S32> edit_bar_height;
+ Optional<S32> padding;
+ Optional<S32> label_width;
+ Optional<F32> min_val_x;
+ Optional<F32> max_val_x;
+ Optional<F32> increment_x;
+ Optional<F32> min_val_y;
+ Optional<F32> max_val_y;
+ Optional<F32> increment_y;
+ Optional<LLUIColor> arrow_color;
+ Optional<LLUIColor> ghost_color;
+ Optional<LLUIColor> area_color;
+ Optional<LLUIColor> grid_color;
+
+ Params();
+ };
+
+
+ virtual ~LLXYVector();
+ /*virtual*/ BOOL postBuild();
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+
+ virtual void draw();
+
+ virtual void setValue(const LLSD& value);
+ void setValue(F32 x, F32 y);
+ virtual LLSD getValue() const;
+
+protected:
+ friend class LLUICtrlFactory;
+ LLXYVector(const Params&);
+ void onEditChange();
+
+protected:
+ LLTextBox* mXLabel;
+ LLTextBox* mYLabel;
+ LLLineEditor* mXEntry;
+ LLLineEditor* mYEntry;
+ LLPanel* mTouchArea;
+ LLViewBorder* mBorder;
+
+private:
+ void update();
+ void setValueAndCommit(F32 x, F32 y);
+
+ F32 mValueX;
+ F32 mValueY;
+
+ F32 mMinValueX;
+ F32 mMaxValueX;
+ F32 mIncrementX;
+ F32 mMinValueY;
+ F32 mMaxValueY;
+ F32 mIncrementY;
+
+ U32 mGhostX;
+ U32 mGhostY;
+
+ LLUIColor mArrowColor;
+ LLUIColor mGhostColor;
+ LLUIColor mAreaColor;
+ LLUIColor mGridColor;
+
+};
+
+#endif
+