Skip to content

Commit bdac91b

Browse files
authored
Merge pull request #317 from gwigz/gwigz/dnd-cof-layers
Add drag-n-drop reordering for clothing layers
2 parents 7870b07 + 3c8d3a6 commit bdac91b

12 files changed

Lines changed: 652 additions & 62 deletions

indra/llui/llflatlistview.cpp

Lines changed: 375 additions & 2 deletions
Large diffs are not rendered by default.

indra/llui/llflatlistview.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llpanel.h"
3131
#include "llscrollcontainer.h"
3232
#include "lltextbox.h"
33+
#include "lluicolor.h"
3334

3435

3536
/**
@@ -106,12 +107,28 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
106107
/** padding between items */
107108
Optional<U32> item_pad;
108109

110+
/** allow items to be reordered by dragging them with the mouse */
111+
Optional<bool> allow_reorder;
112+
113+
/** colour of the insertion indicator drawn while reordering */
114+
Optional<LLUIColor> drag_indicator_color;
115+
109116
/** textbox with info message when list is empty*/
110117
Optional<LLTextBox::Params> no_items_text;
111118

112119
Params();
113120
};
114121

122+
/** Fired after the user drags an item to a new position: (moved value, new visible index). */
123+
typedef boost::function<void(const LLSD& value, S32 new_index)> reorder_signal_t;
124+
125+
/**
126+
* Returns true if the dragged item is allowed to move past the given neighbour.
127+
* Lets owners constrain reordering (e.g. keep items within a group). Optional;
128+
* when unset any reorder is permitted.
129+
*/
130+
typedef boost::function<bool(const LLSD& dragged, const LLSD& neighbour)> reorder_validate_signal_t;
131+
115132
// disable traversal when finding widget to hand focus off to
116133
/*virtual*/ bool canFocusChildren() const override { return false; }
117134

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

285+
/** Turn on/off drag-to-reorder support */
286+
void setAllowReorder(bool allow) { mAllowReorder = allow; }
287+
288+
void setReorderCallback(reorder_signal_t cb) { mReorderCallback = cb; }
289+
void setReorderValidateCallback(reorder_validate_signal_t cb) { mReorderValidateCallback = cb; }
290+
268291
/** Sets flag whether onCommit should be fired if selection was changed */
269292
// FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
270293
void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
@@ -374,6 +397,12 @@ class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
374397

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

400+
virtual bool handleHover(S32 x, S32 y, MASK mask) override;
401+
402+
virtual bool handleMouseUp(S32 x, S32 y, MASK mask) override;
403+
404+
virtual void onMouseCaptureLost() override;
405+
377406
virtual bool postBuild() override;
378407

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

391420
private:
392421

422+
// Drag-to-reorder helpers.
423+
void armReorderDrag(item_pair_t* item_pair);
424+
void updateReorderDrag(S32 x, S32 y);
425+
void finishReorderDrag();
426+
void cancelReorderDrag();
427+
void clearReorderDragState();
428+
429+
// Builds the set of pairs being dragged (the whole selection when the grabbed
430+
// row is part of a multi-selection, constrained to the validator's group).
431+
void buildReorderGroup();
432+
bool isInReorderGroup(item_pair_t* item_pair) const;
433+
434+
// The remaining visible pairs (those not being dragged), in visual order.
435+
void getReorderRemaining(pairs_list_t& remaining) const;
436+
// Number of leading non-dragged items whose centre sits above (x, y).
437+
S32 getInsertIndexAt(S32 x, S32 y) const;
438+
// Clamps an insertion boundary to the validator's contiguous group.
439+
S32 constrainInsertIndex(S32 dest_index) const;
440+
void drawReorderIndicator();
441+
393442
void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
394443

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

434483
bool mFocusOnItemClicked;
435484

485+
/** Drag-to-reorder state */
486+
bool mAllowReorder;
487+
bool mIsReordering; // a drag is currently in progress
488+
bool mProcessingRightClick; // suppress drag arming during right-click handling
489+
item_pair_t* mReorderDragPair; // the grabbed pair (drag anchor)
490+
pairs_list_t mReorderDragGroup; // all pairs being dragged, in visual order
491+
item_pair_t* mDeferredSelectPair; // collapse selection to this on mouse-up if no drag
492+
S32 mReorderMouseDownX;
493+
S32 mReorderMouseDownY;
494+
S32 mReorderInsertIndex; // current drop boundary among the remaining (non-dragged) items
495+
LLUIColor mDragIndicatorColor;
496+
reorder_signal_t mReorderCallback;
497+
reorder_validate_signal_t mReorderValidateCallback;
498+
436499
/** All pairs of the list */
437500
pairs_list_t mItemPairs;
438501

indra/llui/llscrollcontainer.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
9595
mScrolledView(NULL),
9696
mSize(p.size)
9797
{
98+
mStoredDocPos[VERTICAL] = 0;
99+
mStoredDocPos[HORIZONTAL] = 0;
100+
98101
static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
99102
S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
100103

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

200-
mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
201-
mScrollbar[VERTICAL]->setPageSize( visible_height );
202-
203-
mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
204-
mScrollbar[HORIZONTAL]->setPageSize( visible_width );
203+
preserveScrollbarMetrics(VERTICAL, show_v_scrollbar, scrolled_rect.getHeight(), visible_height);
204+
preserveScrollbarMetrics(HORIZONTAL, show_h_scrollbar, scrolled_rect.getWidth(), visible_width);
205205
updateScroll();
206206
}
207207
}
@@ -586,6 +586,24 @@ bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
586586
return ret_val;
587587
}
588588

589+
void LLScrollContainer::preserveScrollbarMetrics(EOrientation axis, bool show, S32 doc_size, S32 page_size)
590+
{
591+
// Snapshot the position before the resize, but only while the scrollbar is
592+
// visible: during a transient full-content pass it hides and its position is
593+
// forced to 0, which would otherwise clobber the remembered position.
594+
if (show && mScrollbar[axis]->getVisible())
595+
{
596+
mStoredDocPos[axis] = mScrollbar[axis]->getDocPos();
597+
}
598+
mScrollbar[axis]->setDocSize(doc_size);
599+
mScrollbar[axis]->setPageSize(page_size);
600+
if (show)
601+
{
602+
mScrollbar[axis]->setDocPos(mStoredDocPos[axis]);
603+
mStoredDocPos[axis] = mScrollbar[axis]->getDocPos();
604+
}
605+
}
606+
589607
void LLScrollContainer::updateScroll()
590608
{
591609
if (!getVisible() || !mScrolledView)
@@ -605,6 +623,9 @@ void LLScrollContainer::updateScroll()
605623
calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
606624

607625
S32 border_width = getBorderWidth();
626+
preserveScrollbarMetrics(VERTICAL, show_v_scrollbar, doc_height, visible_height);
627+
preserveScrollbarMetrics(HORIZONTAL, show_h_scrollbar, doc_width, visible_width);
628+
608629
if( show_v_scrollbar )
609630
{
610631
if( doc_rect.mTop < getRect().getHeight() - border_width )
@@ -667,12 +688,6 @@ void LLScrollContainer::updateScroll()
667688
mScrollbar[HORIZONTAL]->setVisible( false );
668689
mScrollbar[HORIZONTAL]->setDocPos( 0 );
669690
}
670-
671-
mScrollbar[HORIZONTAL]->setDocSize( doc_width );
672-
mScrollbar[HORIZONTAL]->setPageSize( visible_width );
673-
674-
mScrollbar[VERTICAL]->setDocSize( doc_height );
675-
mScrollbar[VERTICAL]->setPageSize( visible_height );
676691
} // end updateScroll
677692

678693
void LLScrollContainer::setBorderVisible(bool b)

indra/llui/llscrollcontainer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ class LLScrollContainer : public LLUICtrl
136136
void updateScroll();
137137
bool autoScroll(S32 x, S32 y, bool do_scroll);
138138
void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const;
139+
void preserveScrollbarMetrics(EOrientation axis, bool show, S32 doc_size, S32 page_size);
139140

140141
LLScrollbar* mScrollbar[ORIENTATION_COUNT];
142+
S32 mStoredDocPos[ORIENTATION_COUNT];
141143
S32 mSize;
142144
bool mIsOpaque;
143145
LLUIColor mBackgroundColor;

indra/newview/llagentwearables.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,23 @@ const S32 LLAgentWearables::getWearableIdxFromItem(const LLViewerInventoryItem*
557557
U32 wearable_count = getWearableCount(type);
558558
if (0 == wearable_count) return -1;
559559

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

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

570579
return -1;
@@ -1604,6 +1613,37 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
16041613
return false;
16051614
}
16061615

1616+
bool LLAgentWearables::moveWearableToIndex(const LLViewerInventoryItem* item, U32 new_index)
1617+
{
1618+
if (!item) return false;
1619+
if (!item->isWearableType()) return false;
1620+
1621+
LLWearableType::EType type = item->getWearableType();
1622+
U32 wearable_count = getWearableCount(type);
1623+
if (wearable_count < 2) return false;
1624+
1625+
if (new_index >= wearable_count) new_index = wearable_count - 1;
1626+
1627+
S32 cur = getWearableIdxFromItem(item);
1628+
if (cur < 0) return false;
1629+
if ((U32)cur == new_index) return true; // already in place
1630+
1631+
// step the wearable to its new slot one swap at a time
1632+
U32 pos = (U32)cur;
1633+
while (pos < new_index)
1634+
{
1635+
if (!swapWearables(type, pos, pos + 1)) return false;
1636+
++pos;
1637+
}
1638+
while (pos > new_index)
1639+
{
1640+
if (!swapWearables(type, pos, pos - 1)) return false;
1641+
--pos;
1642+
}
1643+
1644+
return true;
1645+
}
1646+
16071647
// static
16081648
void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)
16091649
{

indra/newview/llagentwearables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
139139
static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function<void(const LLUUID&)> created_cb = nullptr);
140140
static void editWearable(const LLUUID& item_id);
141141
bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
142+
bool moveWearableToIndex(const LLViewerInventoryItem* item, U32 new_index);
142143

143144
void requestEditingWearable(const LLUUID& item_id);
144145
void editWearableIfRequested(const LLUUID& item_id);

0 commit comments

Comments
 (0)