Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
377 changes: 375 additions & 2 deletions indra/llui/llflatlistview.cpp

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions indra/llui/llflatlistview.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "llpanel.h"
#include "llscrollcontainer.h"
#include "lltextbox.h"
#include "lluicolor.h"


/**
Expand Down Expand Up @@ -106,12 +107,28 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
/** padding between items */
Optional<U32> item_pad;

/** allow items to be reordered by dragging them with the mouse */
Optional<bool> allow_reorder;

/** colour of the insertion indicator drawn while reordering */
Optional<LLUIColor> drag_indicator_color;

/** textbox with info message when list is empty*/
Optional<LLTextBox::Params> no_items_text;

Params();
};

/** Fired after the user drags an item to a new position: (moved value, new visible index). */
typedef boost::function<void(const LLSD& value, S32 new_index)> reorder_signal_t;

/**
* Returns true if the dragged item is allowed to move past the given neighbour.
* Lets owners constrain reordering (e.g. keep items within a group). Optional;
* when unset any reorder is permitted.
*/
typedef boost::function<bool(const LLSD& dragged, const LLSD& neighbour)> reorder_validate_signal_t;

// disable traversal when finding widget to hand focus off to
/*virtual*/ bool canFocusChildren() const override { return false; }

Expand Down Expand Up @@ -265,6 +282,12 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
/** Turn on/off selection support */
void setAllowSelection(bool can_select) { mAllowSelection = can_select; }

/** Turn on/off drag-to-reorder support */
void setAllowReorder(bool allow) { mAllowReorder = allow; }

void setReorderCallback(reorder_signal_t cb) { mReorderCallback = cb; }
void setReorderValidateCallback(reorder_validate_signal_t cb) { mReorderValidateCallback = cb; }

/** Sets flag whether onCommit should be fired if selection was changed */
// FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
Expand Down Expand Up @@ -374,6 +397,12 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler

virtual bool handleKeyHere(KEY key, MASK mask) override;

virtual bool handleHover(S32 x, S32 y, MASK mask) override;

virtual bool handleMouseUp(S32 x, S32 y, MASK mask) override;

virtual void onMouseCaptureLost() override;

virtual bool postBuild() override;

virtual void onFocusReceived() override;
Expand All @@ -390,6 +419,26 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler

private:

// Drag-to-reorder helpers.
void armReorderDrag(item_pair_t* item_pair);
void updateReorderDrag(S32 x, S32 y);
void finishReorderDrag();
void cancelReorderDrag();
void clearReorderDragState();

// Builds the set of pairs being dragged (the whole selection when the grabbed
// row is part of a multi-selection, constrained to the validator's group).
void buildReorderGroup();
bool isInReorderGroup(item_pair_t* item_pair) const;

// The remaining visible pairs (those not being dragged), in visual order.
void getReorderRemaining(pairs_list_t& remaining) const;
// Number of leading non-dragged items whose centre sits above (x, y).
S32 getInsertIndexAt(S32 x, S32 y) const;
// Clamps an insertion boundary to the validator's contiguous group.
S32 constrainInsertIndex(S32 dest_index) const;
void drawReorderIndicator();

void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}

void setNoItemsCommentVisible(bool visible) const;
Expand Down Expand Up @@ -433,6 +482,20 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler

bool mFocusOnItemClicked;

/** Drag-to-reorder state */
bool mAllowReorder;
bool mIsReordering; // a drag is currently in progress
bool mProcessingRightClick; // suppress drag arming during right-click handling
item_pair_t* mReorderDragPair; // the grabbed pair (drag anchor)
pairs_list_t mReorderDragGroup; // all pairs being dragged, in visual order
item_pair_t* mDeferredSelectPair; // collapse selection to this on mouse-up if no drag
S32 mReorderMouseDownX;
S32 mReorderMouseDownY;
S32 mReorderInsertIndex; // current drop boundary among the remaining (non-dragged) items
LLUIColor mDragIndicatorColor;
reorder_signal_t mReorderCallback;
reorder_validate_signal_t mReorderValidateCallback;

/** All pairs of the list */
pairs_list_t mItemPairs;

Expand Down
37 changes: 26 additions & 11 deletions indra/llui/llscrollcontainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mScrolledView(NULL),
mSize(p.size)
{
mStoredDocPos[VERTICAL] = 0;
mStoredDocPos[HORIZONTAL] = 0;

static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);

Expand Down Expand Up @@ -197,11 +200,8 @@ void LLScrollContainer::reshape(S32 width, S32 height,
bool show_h_scrollbar = false;
calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );

mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
mScrollbar[VERTICAL]->setPageSize( visible_height );

mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
mScrollbar[HORIZONTAL]->setPageSize( visible_width );
preserveScrollbarMetrics(VERTICAL, show_v_scrollbar, scrolled_rect.getHeight(), visible_height);
preserveScrollbarMetrics(HORIZONTAL, show_h_scrollbar, scrolled_rect.getWidth(), visible_width);
updateScroll();
}
}
Expand Down Expand Up @@ -586,6 +586,24 @@ bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
return ret_val;
}

void LLScrollContainer::preserveScrollbarMetrics(EOrientation axis, bool show, S32 doc_size, S32 page_size)
{
// Snapshot the position before the resize, but only while the scrollbar is
// visible: during a transient full-content pass it hides and its position is
// forced to 0, which would otherwise clobber the remembered position.
if (show && mScrollbar[axis]->getVisible())
{
mStoredDocPos[axis] = mScrollbar[axis]->getDocPos();
}
mScrollbar[axis]->setDocSize(doc_size);
mScrollbar[axis]->setPageSize(page_size);
if (show)
{
mScrollbar[axis]->setDocPos(mStoredDocPos[axis]);
mStoredDocPos[axis] = mScrollbar[axis]->getDocPos();
}
}

void LLScrollContainer::updateScroll()
{
if (!getVisible() || !mScrolledView)
Expand All @@ -605,6 +623,9 @@ void LLScrollContainer::updateScroll()
calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );

S32 border_width = getBorderWidth();
preserveScrollbarMetrics(VERTICAL, show_v_scrollbar, doc_height, visible_height);
preserveScrollbarMetrics(HORIZONTAL, show_h_scrollbar, doc_width, visible_width);

if( show_v_scrollbar )
{
if( doc_rect.mTop < getRect().getHeight() - border_width )
Expand Down Expand Up @@ -667,12 +688,6 @@ void LLScrollContainer::updateScroll()
mScrollbar[HORIZONTAL]->setVisible( false );
mScrollbar[HORIZONTAL]->setDocPos( 0 );
}

mScrollbar[HORIZONTAL]->setDocSize( doc_width );
mScrollbar[HORIZONTAL]->setPageSize( visible_width );

mScrollbar[VERTICAL]->setDocSize( doc_height );
mScrollbar[VERTICAL]->setPageSize( visible_height );
} // end updateScroll

void LLScrollContainer::setBorderVisible(bool b)
Expand Down
2 changes: 2 additions & 0 deletions indra/llui/llscrollcontainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ class LLScrollContainer : public LLUICtrl
void updateScroll();
bool autoScroll(S32 x, S32 y, bool do_scroll);
void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const;
void preserveScrollbarMetrics(EOrientation axis, bool show, S32 doc_size, S32 page_size);

LLScrollbar* mScrollbar[ORIENTATION_COUNT];
S32 mStoredDocPos[ORIENTATION_COUNT];
S32 mSize;
bool mIsOpaque;
LLUIColor mBackgroundColor;
Expand Down
46 changes: 43 additions & 3 deletions indra/newview/llagentwearables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,14 +557,23 @@ const S32 LLAgentWearables::getWearableIdxFromItem(const LLViewerInventoryItem*
U32 wearable_count = getWearableCount(type);
if (0 == wearable_count) return -1;

const LLUUID& asset_id = item->getAssetUUID();
// Match by linked inventory item id so the correct copy is found when several
// worn wearables of this type share the same asset.
const LLUUID item_id = gInventory.getLinkedItemID(item->getUUID());
for (U32 i = 0; i < wearable_count; ++i)
{
const LLViewerWearable* wearable = getViewerWearable(type, i);
if (!wearable) continue;
if (wearable->getItemID() == item_id) return i;
}

// Fall back to asset id for items whose inventory id can't be resolved.
const LLUUID& asset_id = item->getAssetUUID();
for (U32 i = 0; i < wearable_count; ++i)
{
const LLViewerWearable* wearable = getViewerWearable(type, i);
if (!wearable) continue;
if (wearable->getAssetID() != asset_id) continue;
return i;
if (wearable->getAssetID() == asset_id) return i;
}

return -1;
Expand Down Expand Up @@ -1604,6 +1613,37 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
return false;
}

bool LLAgentWearables::moveWearableToIndex(const LLViewerInventoryItem* item, U32 new_index)
{
if (!item) return false;
if (!item->isWearableType()) return false;

LLWearableType::EType type = item->getWearableType();
U32 wearable_count = getWearableCount(type);
if (wearable_count < 2) return false;

if (new_index >= wearable_count) new_index = wearable_count - 1;

S32 cur = getWearableIdxFromItem(item);
if (cur < 0) return false;
if ((U32)cur == new_index) return true; // already in place

// step the wearable to its new slot one swap at a time
U32 pos = (U32)cur;
while (pos < new_index)
{
if (!swapWearables(type, pos, pos + 1)) return false;
++pos;
}
while (pos > new_index)
{
if (!swapWearables(type, pos, pos - 1)) return false;
--pos;
}

return true;
}

// static
void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)
{
Expand Down
1 change: 1 addition & 0 deletions indra/newview/llagentwearables.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function<void(const LLUUID&)> created_cb = nullptr);
static void editWearable(const LLUUID& item_id);
bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
bool moveWearableToIndex(const LLViewerInventoryItem* item, U32 new_index);

void requestEditingWearable(const LLUUID& item_id);
void editWearableIfRequested(const LLUUID& item_id);
Expand Down
Loading
Loading