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/llfloater.cpp69
-rw-r--r--indra/llui/llfloater.h13
-rw-r--r--indra/llui/llfolderview.cpp27
-rw-r--r--indra/llui/llfolderview.h11
-rw-r--r--indra/llui/llfolderviewitem.cpp1
-rw-r--r--indra/llui/lllineeditor.cpp57
-rw-r--r--indra/llui/lllineeditor.h7
-rw-r--r--indra/llui/llmenugl.cpp22
-rw-r--r--indra/llui/llmenugl.h3
-rw-r--r--indra/llui/llmultislider.cpp438
-rw-r--r--indra/llui/llmultislider.h52
-rw-r--r--indra/llui/llmultisliderctrl.cpp35
-rw-r--r--indra/llui/llmultisliderctrl.h28
-rw-r--r--indra/llui/llslider.cpp7
-rw-r--r--indra/llui/llslider.h4
-rw-r--r--indra/llui/llsliderctrl.cpp41
-rw-r--r--indra/llui/llsliderctrl.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.cpp337
-rw-r--r--indra/llui/llxyvector.h122
23 files changed, 1785 insertions, 142 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index e44f57fa9f..730e277dec 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
@@ -250,8 +252,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/llfloater.cpp b/indra/llui/llfloater.cpp
index 42802cd339..e3bb36192c 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -37,6 +37,7 @@
#include "lluictrlfactory.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
+#include "llcriticaldamp.h" // LLSmoothInterpolation
#include "lldir.h"
#include "lldraghandle.h"
#include "llfloaterreg.h"
@@ -64,6 +65,10 @@
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
+const F32 LLFloater::CONTEXT_CONE_IN_ALPHA = 0.0f;
+const F32 LLFloater::CONTEXT_CONE_OUT_ALPHA = 1.f;
+const F32 LLFloater::CONTEXT_CONE_FADE_TIME = 0.08f;
+
namespace LLInitParam
{
void TypeValues<LLFloaterEnums::EOpenPositioning>::declareValues()
@@ -2116,6 +2121,70 @@ void LLFloater::updateTitleButtons()
}
}
+void LLFloater::drawConeToOwner(F32 &context_cone_opacity,
+ F32 max_cone_opacity,
+ LLView *owner_view,
+ F32 fade_time,
+ F32 contex_cone_in_alpha,
+ F32 contex_cone_out_alpha)
+{
+ if (owner_view
+ && owner_view->isInVisibleChain()
+ && hasFocus()
+ && context_cone_opacity > 0.001f
+ && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // draw cone of context pointing back to owner (e.x. texture swatch)
+ LLRect owner_rect;
+ owner_view->localRectToOtherView(owner_view->getLocalRect(), &owner_rect, this);
+ LLRect local_rect = getLocalRect();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ LLGLEnable(GL_CULL_FACE);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
+
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ }
+ gGL.end();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(getDragHandle()))
+ {
+ context_cone_opacity = lerp(context_cone_opacity, max_cone_opacity, LLSmoothInterpolation::getInterpolant(fade_time));
+ }
+ else
+ {
+ context_cone_opacity = lerp(context_cone_opacity, 0.f, LLSmoothInterpolation::getInterpolant(fade_time));
+ }
+}
+
void LLFloater::buildButtons(const Params& floater_params)
{
static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 165f67499b..f8c04e8a2f 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -395,6 +395,15 @@ protected:
virtual void updateTitleButtons();
+ // Draws a cone from this floater to parent floater or view (owner)
+ // Modifies context_cone_opacity (interpolates according to fade time and returns new value)
+ void drawConeToOwner(F32 &context_cone_opacity,
+ F32 max_cone_opacity,
+ LLView *owner_view,
+ F32 context_fade_time = CONTEXT_CONE_FADE_TIME,
+ F32 contex_cone_in_alpha = CONTEXT_CONE_IN_ALPHA,
+ F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA);
+
private:
void setForeground(BOOL b); // called only by floaterview
void cleanupHandles(); // remove handles to dead floaters
@@ -424,6 +433,10 @@ private:
void updateTransparency(LLView* view, ETypeTransparency transparency_type);
public:
+ static const F32 CONTEXT_CONE_IN_ALPHA;
+ static const F32 CONTEXT_CONE_OUT_ALPHA;
+ static const F32 CONTEXT_CONE_FADE_TIME;
+
// Called when floater is opened, passes mKey
// Public so external views or floaters can watch for this floater opening
commit_signal_t mOpenSignal;
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index b05a9807d0..e718fcec46 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -148,7 +148,9 @@ LLFolderView::Params::Params()
: title("title"),
use_label_suffix("use_label_suffix"),
allow_multiselect("allow_multiselect", true),
+ allow_drag("allow_drag", true),
show_empty_message("show_empty_message", true),
+ suppress_folder_menu("suppress_folder_menu", false),
use_ellipses("use_ellipses", false),
options_menu("options_menu", "")
{
@@ -162,11 +164,13 @@ LLFolderView::LLFolderView(const Params& p)
mScrollContainer( NULL ),
mPopupMenuHandle(),
mAllowMultiSelect(p.allow_multiselect),
+ mAllowDrag(p.allow_drag),
mShowEmptyMessage(p.show_empty_message),
mShowFolderHierarchy(FALSE),
mRenameItem( NULL ),
mNeedsScroll( FALSE ),
mUseLabelSuffix(p.use_label_suffix),
+ mSuppressFolderMenu(p.suppress_folder_menu),
mPinningSelectedItem(FALSE),
mNeedsAutoSelect( FALSE ),
mAutoSelectOverride(FALSE),
@@ -1432,10 +1436,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 +1456,7 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
if (mCallbackRegistrar)
{
mCallbackRegistrar->popScope();
- }
+ }
}
else
{
@@ -1862,6 +1869,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..6bb5e6c02e 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -90,9 +90,11 @@ public:
Optional<std::string> title;
Optional<bool> use_label_suffix,
allow_multiselect,
+ allow_drag,
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;
@@ -123,6 +125,7 @@ public:
void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); }
bool getAllowMultiSelect() { return mAllowMultiSelect; }
+ bool getAllowDrag() { return mAllowDrag; }
// Close all folders in the view
void closeAllFolders();
@@ -259,6 +262,8 @@ protected:
void closeRenamer( void );
+ bool isFolderSelected();
+
bool selectFirstItem();
bool selectLastItem();
@@ -271,6 +276,7 @@ protected:
selected_items_t mSelectedItems;
bool mKeyboardSelection,
mAllowMultiSelect,
+ mAllowDrag,
mShowEmptyMessage,
mShowFolderHierarchy,
mNeedsScroll,
@@ -282,7 +288,8 @@ protected:
mDragAndDropThisFrame,
mShowItemLinkOverlays,
mShowSelectionContext,
- mShowSingleSelection;
+ mShowSingleSelection,
+ mSuppressFolderMenu;
// Renaming variables and methods
LLFolderViewItem* mRenameItem; // The item currently being renamed
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 2de47f1a19..9a1f7de73b 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -557,6 +557,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
LLFolderView* root = getRoot();
if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold()
+ && root->getAllowDrag()
&& root->getCurSelectedItem()
&& root->startDrag())
{
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 3ad504d68d..70304cdfd2 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -96,6 +96,8 @@ LLLineEditor::Params::Params()
ignore_tab("ignore_tab", true),
is_password("is_password", false),
cursor_color("cursor_color"),
+ use_bg_color("use_bg_color", false),
+ bg_color("bg_color"),
text_color("text_color"),
text_readonly_color("text_readonly_color"),
text_tentative_color("text_tentative_color"),
@@ -150,10 +152,12 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mBgImageDisabled( p.background_image_disabled ),
mBgImageFocused( p.background_image_focused ),
mShowImageFocused( p.bg_image_always_focused ),
+ mUseBgColor(p.use_bg_color),
mHaveHistory(FALSE),
mReplaceNewlinesWithSpaces( TRUE ),
mLabel(p.label),
mCursorColor(p.cursor_color()),
+ mBgColor(p.bg_color()),
mFgColor(p.text_color()),
mReadOnlyFgColor(p.text_readonly_color()),
mTentativeFgColor(p.text_tentative_color()),
@@ -1688,37 +1692,42 @@ void LLLineEditor::doDelete()
void LLLineEditor::drawBackground()
{
- bool has_focus = hasFocus();
- LLUIImage* image;
- if ( mReadOnly )
- {
- image = mBgImageDisabled;
- }
- else if ( has_focus || mShowImageFocused)
+ F32 alpha = getCurrentTransparency();
+ if (mUseBgColor)
{
- image = mBgImageFocused;
+ gl_rect_2d(getLocalRect(), mBgColor % alpha, TRUE);
}
else
{
- image = mBgImage;
- }
-
- if (!image) return;
-
- F32 alpha = getCurrentTransparency();
+ bool has_focus = hasFocus();
+ LLUIImage* image;
+ if (mReadOnly)
+ {
+ image = mBgImageDisabled;
+ }
+ else if (has_focus || mShowImageFocused)
+ {
+ image = mBgImageFocused;
+ }
+ else
+ {
+ image = mBgImage;
+ }
- // optionally draw programmatic border
- if (has_focus)
- {
- LLColor4 tmp_color = gFocusMgr.getFocusColor();
+ if (!image) return;
+ // optionally draw programmatic border
+ if (has_focus)
+ {
+ LLColor4 tmp_color = gFocusMgr.getFocusColor();
+ tmp_color.setAlpha(alpha);
+ image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
+ tmp_color,
+ gFocusMgr.getFocusFlashWidth());
+ }
+ LLColor4 tmp_color = UI_VERTEX_COLOR;
tmp_color.setAlpha(alpha);
- image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
- tmp_color,
- gFocusMgr.getFocusFlashWidth());
+ image->draw(getLocalRect(), tmp_color);
}
- LLColor4 tmp_color = UI_VERTEX_COLOR;
- tmp_color.setAlpha(alpha);
- image->draw(getLocalRect(), tmp_color);
}
void LLLineEditor::draw()
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index f775d53e63..aa5779d45f 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -91,10 +91,12 @@ public:
commit_on_focus_lost,
ignore_tab,
bg_image_always_focused,
- is_password;
+ is_password,
+ use_bg_color;
// colors
Optional<LLUIColor> cursor_color,
+ bg_color,
text_color,
text_readonly_color,
text_tentative_color,
@@ -368,6 +370,7 @@ protected:
LLTimer mTripleClickTimer;
LLUIColor mCursorColor;
+ LLUIColor mBgColor;
LLUIColor mFgColor;
LLUIColor mReadOnlyFgColor;
LLUIColor mTentativeFgColor;
@@ -388,6 +391,8 @@ protected:
BOOL mShowImageFocused;
+ bool mUseBgColor;
+
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector<S32> mPreeditPositions;
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 732fa9feb1..5568a84494 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -2723,6 +2723,15 @@ void LLMenuGL::setItemVisible( const std::string& name, BOOL visible )
}
}
+
+void LLMenuGL::setItemLabel(const std::string &name, const std::string &label)
+{
+ LLMenuItemGL *item = getItem(name);
+
+ if (item)
+ item->setLabel(label);
+}
+
void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
{
if (getVisible())
@@ -2767,6 +2776,19 @@ LLMenuItemGL* LLMenuGL::getItem(S32 number)
return NULL;
}
+LLMenuItemGL* LLMenuGL::getItem(std::string name)
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getName() == name)
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
LLMenuItemGL* LLMenuGL::getHighlightedItem()
{
item_list_t::iterator item_iter;
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 78f688642e..1f11f26192 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -468,6 +468,8 @@ public:
void setEnabledSubMenus(BOOL enable);
void setItemVisible( const std::string& name, BOOL visible);
+
+ void setItemLabel(const std::string &name, const std::string &label);
// sets the left,bottom corner of menu, useful for popups
void setLeftAndBottom(S32 left, S32 bottom);
@@ -498,6 +500,7 @@ public:
void setItemLastSelected(LLMenuItemGL* item); // must be in menu
U32 getItemCount(); // number of menu items
LLMenuItemGL* getItem(S32 number); // 0 = first item
+ LLMenuItemGL* getItem(std::string name);
LLMenuItemGL* getHighlightedItem();
LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled = TRUE);
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp
index 0aa3e17075..acfe4a0cba 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() && p.overlap_threshold > mIncrement)
+ {
+ mOverlapThreshold = p.overlap_threshold - mIncrement;
+ }
+ 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()
@@ -119,6 +149,16 @@ LLMultiSlider::~LLMultiSlider()
delete mMouseUpSignal;
}
+F32 LLMultiSlider::getNearestIncrement(F32 value) const
+{
+ value = llclamp(value, mMinValue, mMaxValue);
+
+ // Round to nearest increment (bias towards rounding down)
+ value -= mMinValue;
+ value += mIncrement / 2.0001f;
+ value -= fmod(value, mIncrement);
+ return mMinValue + value;
+}
void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
{
@@ -127,13 +167,7 @@ void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from
return;
}
- value = llclamp( value, mMinValue, mMaxValue );
-
- // Round to nearest increment (bias towards rounding down)
- value -= mMinValue;
- value += mIncrement/2.0001f;
- value -= fmod(value, mIncrement);
- F32 newValue = mMinValue + value;
+ F32 newValue = getNearestIncrement(value);
// now, make sure no overlap
// if we want that
@@ -143,14 +177,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 +230,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 +269,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 +283,62 @@ 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);
+}
+
+
+LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const
+{
+ auto it = mThumbRects.find(name);
+ if (it != mThumbRects.end())
+ return (*it).second;
+ return LLRect();
+}
+
+void LLMultiSlider::setSliderThumbImage(const std::string &name)
+{
+ if (!name.empty())
+ {
+ mThumbImagep = LLUI::getUIImage(name);
+ }
+ else
+ clearSliderThumbImage();
+}
+
+void LLMultiSlider::clearSliderThumbImage()
+{
+ mThumbImagep = NULL;
+}
+
+void LLMultiSlider::resetCurSlider()
+{
+ mCurSlider = LLStringUtil::null;
+}
+
const std::string& LLMultiSlider::addSlider()
{
return addSlider(mInitialValue);
@@ -230,7 +363,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);
@@ -242,21 +382,28 @@ const std::string& LLMultiSlider::addSlider(F32 val)
return mCurSlider;
}
-void LLMultiSlider::addSlider(F32 val, const std::string& name)
+bool LLMultiSlider::addSlider(F32 val, const std::string& name)
{
F32 initVal = val;
if(mValue.size() >= mMaxNumSliders) {
- return;
+ return false;
}
bool foundOne = findUnusedValue(initVal);
if(!foundOne) {
- return;
+ return false;
}
// 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);
@@ -264,6 +411,8 @@ void LLMultiSlider::addSlider(F32 val, const std::string& name)
// move the slider
setSliderValue(mCurSlider, initVal, TRUE);
+
+ return true;
}
bool LLMultiSlider::findUnusedValue(F32& initVal)
@@ -278,11 +427,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 +485,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 +501,7 @@ 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 );
+ setCurSliderValue(getSliderValueFromPos(x, y));
onCommit();
getWindow()->setCursor(UI_CURSOR_ARROW);
@@ -360,6 +509,24 @@ BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
}
else
{
+ if (getEnabled())
+ {
+ mHoverSlider.clear();
+ std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
+ for (; mIt != mThumbRects.end(); mIt++)
+ {
+ if (mIt->second.pointInRect(x, y))
+ {
+ mHoverSlider = mIt->first;
+ break;
+ }
+ }
+ }
+ else
+ {
+ mHoverSlider.clear();
+ }
+
getWindow()->setCursor(UI_CURSOR_ARROW);
LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
}
@@ -416,20 +583,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))
+ if (!mCurSlider.empty())
{
- mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth/2) - x;
- }
- else
- {
- 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");
@@ -462,6 +639,13 @@ BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask)
return handled;
}
+/*virtual*/
+void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ mHoverSlider.clear();
+ LLF32UICtrl::onMouseLeave(x, y, mask);
+}
+
void LLMultiSlider::draw()
{
static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
@@ -470,6 +654,7 @@ void LLMultiSlider::draw()
std::map<std::string, LLRect>::iterator mIt;
std::map<std::string, LLRect>::iterator curSldrIt;
+ std::map<std::string, LLRect>::iterator hoverSldrIt;
// Draw background and thumb.
@@ -483,9 +668,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,10 +704,11 @@ void LLMultiSlider::draw()
mTriangleColor.get() % opacity, TRUE);
}
}
- else if (!thumb_imagep)
+ else if (!thumb_imagep && !mThumbImagep)
{
// draw all the thumbs
curSldrIt = mThumbRects.end();
+ hoverSldrIt = mThumbRects.end();
for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
// choose the color
@@ -522,15 +717,21 @@ void LLMultiSlider::draw()
curSldrIt = mIt;
continue;
- //curThumbColor = mThumbCenterSelectedColor;
+ }
+ if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
+ {
+ // draw last, after current one
+ hoverSldrIt = mIt;
+ continue;
}
// the draw command
gl_rect_2d(mIt->second, curThumbColor, TRUE);
}
- // now draw the current slider
- if(curSldrIt != mThumbRects.end()) {
+ // now draw the current and hover sliders
+ if(curSldrIt != mThumbRects.end())
+ {
gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
}
@@ -539,20 +740,57 @@ void LLMultiSlider::draw()
{
gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE);
}
+ else if (hoverSldrIt != mThumbRects.end())
+ {
+ gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
+ }
}
- 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())
{
- thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ if (!mCurSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
+ }
}
+ if (!mHoverSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ thumb_imagep->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
+ }
// draw the thumbs
curSldrIt = mThumbRects.end();
+ hoverSldrIt = mThumbRects.end();
for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
{
// choose the color
@@ -563,46 +801,68 @@ void LLMultiSlider::draw()
curSldrIt = mIt;
continue;
}
+ if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
+ {
+ // don't draw now, draw last, after current one
+ hoverSldrIt = mIt;
+ continue;
+ }
// 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
+ // draw cur and hover 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 (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);
+ }
}
-
- if(curSldrIt != mThumbRects.end())
+ if(hoverSldrIt != mThumbRects.end())
{
- thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
+ if (mThumbImagep)
+ {
+ mThumbImagep->draw(hoverSldrIt->second);
+ }
+ else
+ {
+ thumb_imagep->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get());
+ }
}
}
diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h
index 2b422e89c9..99a78d6e09 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;
@@ -70,16 +77,27 @@ protected:
friend class LLUICtrlFactory;
public:
virtual ~LLMultiSlider();
+
+ // Multi-slider rounds values to nearest increments (bias towards rounding down)
+ F32 getNearestIncrement(F32 value) const;
+
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;
+ LLRect getSliderThumbRect(const std::string& name) const;
+
+ void setSliderThumbImage(const std::string &name);
+ void clearSliderThumbImage();
+
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);
- /*virtual*/ LLSD getValue() const { return mValue; }
+ /*virtual*/ void setValue(const LLSD& value) override;
+ /*virtual*/ LLSD getValue() const override { return mValue; }
boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
@@ -87,24 +105,34 @@ public:
bool findUnusedValue(F32& initVal);
const std::string& addSlider();
const std::string& addSlider(F32 val);
- void addSlider(F32 val, const std::string& name);
+ bool addSlider(F32 val, const std::string& name);
void deleteSlider(const std::string& name);
void deleteCurSlider() { deleteSlider(mCurSlider); }
- void clear();
+ /*virtual*/ void clear() override;
+
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask) override;
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ void draw() override;
+
+ S32 getMaxNumSliders() { return mMaxNumSliders; }
+ S32 getCurNumSliders() { return mValue.size(); }
+ F32 getOverlapThreshold() { return mOverlapThreshold; }
+ bool canAddSliders() { return mValue.size() < mMaxNumSliders; }
- /*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 handleKeyHere(KEY key, MASK mask);
- /*virtual*/ void draw();
protected:
LLSD mValue;
std::string mCurSlider;
+ std::string mHoverSlider;
static S32 mNameCounter;
S32 mMaxNumSliders;
BOOL mAllowOverlap;
+ BOOL mLoopOverlap;
+ F32 mOverlapThreshold;
BOOL mDrawTrack;
BOOL mUseTriangle; /// hacked in toggle to use a triangle
@@ -116,11 +144,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..20e2b569f1 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;
@@ -269,6 +293,17 @@ const std::string& LLMultiSliderCtrl::addSlider(F32 val)
return name;
}
+bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name)
+{
+ bool res = mMultiSlider->addSlider(val, name);
+ if (res)
+ {
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+ }
+ return res;
+}
+
void LLMultiSliderCtrl::deleteSlider(const std::string& name)
{
mMultiSlider->deleteSlider(name);
diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h
index b6a3542376..adb28676ec 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,15 +107,28 @@ public:
void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);}
void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);}
+ F32 getNearestIncrement(F32 value) const { return mMultiSlider->getNearestIncrement(value); }
+ F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); }
+ LLRect getSliderThumbRect(const std::string &name) const { return mMultiSlider->getSliderThumbRect(name); }
+
+ void setSliderThumbImage(const std::string &name) { mMultiSlider->setSliderThumbImage(name); }
+ void clearSliderThumbImage() { mMultiSlider->clearSliderThumbImage(); }
+
/// for adding and deleting sliders
const std::string& addSlider();
const std::string& addSlider(F32 val);
+ bool addSlider(F32 val, const std::string& name);
void deleteSlider(const std::string& name);
void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); }
F32 getMinValue() const { return mMultiSlider->getMinValue(); }
F32 getMaxValue() const { return mMultiSlider->getMaxValue(); }
+ S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); }
+ S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); }
+ F32 getOverlapThreshold() { return mMultiSlider->getOverlapThreshold(); }
+ 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/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 0056cb6dc4..3b89a8ca63 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -283,6 +283,35 @@ void LLSliderCtrl::updateText()
}
}
+void LLSliderCtrl::updateSliderRect()
+{
+ S32 right = getRect().getWidth();
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
+ S32 left = 0;
+ static LLUICachedControl<S32> sliderctrl_spacing("UISliderctrlSpacing", 0);
+ if (mEditor)
+ {
+ LLRect editor_rect = mEditor->getRect();
+ S32 editor_width = editor_rect.getWidth();
+ editor_rect.mRight = right;
+ editor_rect.mLeft = right - editor_width;
+ mEditor->setRect(editor_rect);
+
+ right -= editor_width + sliderctrl_spacing;
+ }
+ if (mTextBox)
+ {
+ right -= mTextBox->getRect().getWidth() + sliderctrl_spacing;
+ }
+ if (mLabelBox)
+ {
+ left += mLabelBox->getRect().getWidth() + sliderctrl_spacing;
+ }
+
+ mSlider->setRect(LLRect(left, top,right,bottom));
+}
+
// static
void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata )
{
@@ -404,6 +433,18 @@ void LLSliderCtrl::onCommit()
LLF32UICtrl::onCommit();
}
+void LLSliderCtrl::setRect(const LLRect& rect)
+{
+ LLF32UICtrl::setRect(rect);
+ updateSliderRect();
+}
+
+//virtual
+void LLSliderCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLF32UICtrl::reshape(width, height, called_from_parent);
+ updateSliderRect();
+}
void LLSliderCtrl::setPrecision(S32 precision)
{
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 2bb8668b90..541c167717 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -125,6 +125,9 @@ public:
mSlider->setControlName(control_name, context);
}
+ /*virtual*/ void setRect(const LLRect& rect);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata);
static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata);
@@ -146,6 +149,7 @@ protected:
}
private:
void updateText();
+ void updateSliderRect();
void reportInvalidData();
const LLFontGL* mFont;
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index e1c51deba9..9856e551cc 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..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);
+ }
+}
+
diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h
new file mode 100644
index 0000000000..bb3822dd26
--- /dev/null
+++ b/indra/llui/llxyvector.h
@@ -0,0 +1,122 @@
+/**
+* @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;
+ Optional<BOOL> logarithmic;
+
+ 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;
+
+ BOOL mLogarithmic;
+ F32 mLogScaleX;
+ F32 mLogScaleY;
+};
+
+#endif
+