1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Tweaks to ListBox so that if its viewport is in drag-to-scroll mode, the selection automatically happens on mouse-up so not to interfere with dragging. (Also a bit of modernisation of the class internals)

This commit is contained in:
jules 2017-04-21 12:24:51 +01:00
parent 320c8002da
commit 6bb3d9b1db
2 changed files with 104 additions and 127 deletions

View file

@ -26,15 +26,11 @@ class ListBox::RowComponent : public Component,
public TooltipClient
{
public:
RowComponent (ListBox& lb)
: owner (lb), row (-1),
selected (false), isDragging (false), selectRowOnMouseUp (false)
{
}
RowComponent (ListBox& lb) : owner (lb) {}
void paint (Graphics& g) override
{
if (ListBoxModel* m = owner.getModel())
if (auto* m = owner.getModel())
m->paintListBoxItem (row, g, getWidth(), getHeight(), selected);
}
@ -47,7 +43,7 @@ public:
selected = nowSelected;
}
if (ListBoxModel* m = owner.getModel())
if (auto* m = owner.getModel())
{
setMouseCursor (m->getMouseCursorForRow (row));
@ -61,48 +57,53 @@ public:
}
}
void performSelection (const MouseEvent& e, bool isMouseUp)
{
owner.selectRowsBasedOnModifierKeys (row, e.mods, isMouseUp);
if (auto* m = owner.getModel())
m->listBoxItemClicked (row, e);
}
bool isInDragToScrollViewport() const noexcept
{
if (auto* vp = owner.getViewport())
return vp->isScrollOnDragEnabled() && (vp->canScrollVertically() || vp->canScrollHorizontally());
return false;
}
void mouseDown (const MouseEvent& e) override
{
isDragging = false;
isDraggingToScroll = false;
selectRowOnMouseUp = false;
if (isEnabled())
{
if (owner.selectOnMouseDown && ! selected)
{
owner.selectRowsBasedOnModifierKeys (row, e.mods, false);
if (ListBoxModel* m = owner.getModel())
m->listBoxItemClicked (row, e);
}
if (owner.selectOnMouseDown && ! (selected || isInDragToScrollViewport()))
performSelection (e, false);
else
{
selectRowOnMouseUp = true;
}
}
}
void mouseUp (const MouseEvent& e) override
{
if (isEnabled() && selectRowOnMouseUp && ! isDragging)
{
owner.selectRowsBasedOnModifierKeys (row, e.mods, true);
if (ListBoxModel* m = owner.getModel())
m->listBoxItemClicked (row, e);
}
if (isEnabled() && selectRowOnMouseUp && ! (isDragging || isDraggingToScroll))
performSelection (e, true);
}
void mouseDoubleClick (const MouseEvent& e) override
{
if (ListBoxModel* m = owner.getModel())
if (isEnabled())
if (isEnabled())
if (auto* m = owner.getModel())
m->listBoxItemDoubleClicked (row, e);
}
void mouseDrag (const MouseEvent& e) override
{
if (ListBoxModel* m = owner.getModel())
if (auto* m = owner.getModel())
{
if (isEnabled() && e.mouseWasDraggedSinceMouseDown() && ! isDragging)
{
@ -115,7 +116,7 @@ public:
if (rowsToDrag.size() > 0)
{
const var dragDescription (m->getDragSourceDescription (rowsToDrag));
auto dragDescription = m->getDragSourceDescription (rowsToDrag);
if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty())))
{
@ -125,6 +126,10 @@ public:
}
}
}
if (! isDraggingToScroll)
if (auto* vp = owner.getViewport())
isDraggingToScroll = vp->isCurrentlyScrollingOnDrag();
}
void resized() override
@ -135,18 +140,16 @@ public:
String getTooltip() override
{
if (ListBoxModel* m = owner.getModel())
if (auto* m = owner.getModel())
return m->getTooltipForRow (row);
return {};
}
ScopedPointer<Component> customComponent;
private:
ListBox& owner;
int row;
bool selected, isDragging, selectRowOnMouseUp;
ScopedPointer<Component> customComponent;
int row = -1;
bool selected = false, isDragging = false, isDraggingToScroll = false, selectRowOnMouseUp = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RowComponent)
};
@ -156,12 +159,11 @@ private:
class ListBox::ListViewport : public Viewport
{
public:
ListViewport (ListBox& lb)
: owner (lb)
ListViewport (ListBox& lb) : owner (lb)
{
setWantsKeyboardFocus (false);
Component* const content = new Component();
auto content = new Component();
setViewedComponent (content);
content->setWantsKeyboardFocus (false);
}
@ -193,7 +195,7 @@ public:
{
updateVisibleArea (true);
if (ListBoxModel* m = owner.getModel())
if (auto* m = owner.getModel())
m->listWasScrolled();
}
@ -201,11 +203,11 @@ public:
{
hasUpdated = false;
Component& content = *getViewedComponent();
const int newX = content.getX();
int newY = content.getY();
const int newW = jmax (owner.minimumRowWidth, getMaximumVisibleWidth());
const int newH = owner.totalItems * owner.getRowHeight();
auto& content = *getViewedComponent();
auto newX = content.getX();
auto newY = content.getY();
auto newW = jmax (owner.minimumRowWidth, getMaximumVisibleWidth());
auto newH = owner.totalItems * owner.getRowHeight();
if (newY + newH < getMaximumVisibleHeight() && newH > getMaximumVisibleHeight())
newY = getMaximumVisibleHeight() - newH;
@ -219,20 +221,20 @@ public:
void updateContents()
{
hasUpdated = true;
const int rowH = owner.getRowHeight();
Component& content = *getViewedComponent();
auto rowH = owner.getRowHeight();
auto& content = *getViewedComponent();
if (rowH > 0)
{
const int y = getViewPositionY();
const int w = content.getWidth();
auto y = getViewPositionY();
auto w = content.getWidth();
const int numNeeded = 2 + getMaximumVisibleHeight() / rowH;
rows.removeRange (numNeeded, rows.size());
while (numNeeded > rows.size())
{
RowComponent* newRow = new RowComponent (owner);
auto newRow = new RowComponent (owner);
rows.add (newRow);
content.addAndMakeVisible (newRow);
}
@ -245,7 +247,7 @@ public:
{
const int row = i + firstIndex;
if (RowComponent* const rowComp = getComponentForRow (row))
if (auto* rowComp = getComponentForRow (row))
{
rowComp->setBounds (0, row * rowH, w, rowH);
rowComp->update (row, owner.isRowSelected (row));
@ -331,16 +333,15 @@ public:
private:
ListBox& owner;
OwnedArray<RowComponent> rows;
int firstIndex, firstWholeIndex, lastWholeIndex;
bool hasUpdated;
int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0;
bool hasUpdated = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListViewport)
};
//==============================================================================
class ListBoxMouseMoveSelector : public MouseListener
struct ListBoxMouseMoveSelector : public MouseListener
{
public:
ListBoxMouseMoveSelector (ListBox& lb) : owner (lb)
{
owner.addMouseListener (this, true);
@ -353,8 +354,8 @@ public:
void mouseMove (const MouseEvent& e) override
{
const MouseEvent e2 (e.getEventRelativeTo (&owner));
owner.selectRow (owner.getRowContainingPosition (e2.x, e2.y), true);
auto pos = e.getEventRelativeTo (&owner).position.toInt();
owner.selectRow (owner.getRowContainingPosition (pos.x, pos.y), true);
}
void mouseExit (const MouseEvent& e) override
@ -362,26 +363,14 @@ public:
mouseMove (e);
}
private:
ListBox& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListBoxMouseMoveSelector)
};
//==============================================================================
ListBox::ListBox (const String& name, ListBoxModel* const m)
: Component (name),
model (m),
totalItems (0),
rowHeight (22),
minimumRowWidth (0),
outlineThickness (0),
lastRowSelected (-1),
multipleSelection (false),
alwaysFlipSelection (false),
hasDoneInitialUpdate (false),
selectOnMouseDown (true)
: Component (name), model (m)
{
addAndMakeVisible (viewport = new ListViewport (*this));
@ -470,7 +459,7 @@ void ListBox::updateContent()
if (selected.size() > 0 && selected [selected.size() - 1] >= totalItems)
{
selected.removeRange (Range<int> (totalItems, std::numeric_limits<int>::max()));
selected.removeRange ({ totalItems, std::numeric_limits<int>::max() });
lastRowSelected = getSelectedRow (0);
selectionChanged = true;
}
@ -504,7 +493,7 @@ void ListBox::selectRowInternal (const int row,
if (deselectOthersFirst)
selected.clear();
selected.addRange (Range<int> (row, row + 1));
selected.addRange ({ row, row + 1 });
if (getHeight() == 0 || getWidth() == 0)
dontScroll = true;
@ -527,7 +516,7 @@ void ListBox::deselectRow (const int row)
{
if (selected.contains (row))
{
selected.removeRange (Range<int> (row, row + 1));
selected.removeRange ({ row, row + 1 });
if (row == lastRowSelected)
lastRowSelected = getSelectedRow (0);
@ -541,7 +530,7 @@ void ListBox::setSelectedRows (const SparseSet<int>& setOfRowsToBeSelected,
const NotificationType sendNotificationEventToModel)
{
selected = setOfRowsToBeSelected;
selected.removeRange (Range<int> (totalItems, std::numeric_limits<int>::max()));
selected.removeRange ({ totalItems, std::numeric_limits<int>::max() });
if (! isRowSelected (lastRowSelected))
lastRowSelected = getSelectedRow (0);
@ -565,10 +554,10 @@ void ListBox::selectRangeOfRows (int firstRow, int lastRow, bool dontScrollToSho
firstRow = jlimit (0, jmax (0, numRows), firstRow);
lastRow = jlimit (0, jmax (0, numRows), lastRow);
selected.addRange (Range<int> (jmin (firstRow, lastRow),
jmax (firstRow, lastRow) + 1));
selected.addRange ({ jmin (firstRow, lastRow),
jmax (firstRow, lastRow) + 1 });
selected.removeRange (Range<int> (lastRow, lastRow + 1));
selected.removeRange ({ lastRow, lastRow + 1 });
}
selectRowInternal (lastRow, dontScrollToShowThisRange, false, true);
@ -652,18 +641,15 @@ int ListBox::getRowContainingPosition (const int x, const int y) const noexcept
int ListBox::getInsertionIndexForPosition (const int x, const int y) const noexcept
{
if (isPositiveAndBelow (x, getWidth()))
{
const int row = (viewport->getViewPositionY() + y + rowHeight / 2 - viewport->getY()) / rowHeight;
return jlimit (0, totalItems, row);
}
return jlimit (0, totalItems, (viewport->getViewPositionY() + y + rowHeight / 2 - viewport->getY()) / rowHeight);
return -1;
}
Component* ListBox::getComponentForRowNumber (const int row) const noexcept
{
if (RowComponent* const listRowComp = viewport->getComponentForRowIfOnscreen (row))
return static_cast<Component*> (listRowComp->customComponent);
if (auto* listRowComp = viewport->getComponentForRowIfOnscreen (row))
return listRowComp->customComponent;
return nullptr;
}
@ -673,21 +659,20 @@ int ListBox::getRowNumberOfComponent (Component* const rowComponent) const noexc
return viewport->getRowNumberOfComponent (rowComponent);
}
Rectangle<int> ListBox::getRowPosition (const int rowNumber,
const bool relativeToComponentTopLeft) const noexcept
Rectangle<int> ListBox::getRowPosition (int rowNumber, bool relativeToComponentTopLeft) const noexcept
{
int y = viewport->getY() + rowHeight * rowNumber;
auto y = viewport->getY() + rowHeight * rowNumber;
if (relativeToComponentTopLeft)
y -= viewport->getViewPositionY();
return Rectangle<int> (viewport->getX(), y,
viewport->getViewedComponent()->getWidth(), rowHeight);
return { viewport->getX(), y,
viewport->getViewedComponent()->getWidth(), rowHeight };
}
void ListBox::setVerticalPosition (const double proportion)
{
const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
auto offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
viewport->setViewPosition (viewport->getViewPositionX(),
jmax (0, roundToInt (proportion * offscreen)));
@ -695,10 +680,10 @@ void ListBox::setVerticalPosition (const double proportion)
double ListBox::getVerticalPosition() const
{
const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
auto offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
return (offscreen > 0) ? viewport->getViewPositionY() / (double) offscreen
: 0;
return offscreen > 0 ? viewport->getViewPositionY() / (double) offscreen
: 0;
}
int ListBox::getVisibleRowWidth() const noexcept
@ -842,20 +827,10 @@ void ListBox::setMinimumContentWidth (const int newMinimumWidth)
updateContent();
}
int ListBox::getVisibleContentWidth() const noexcept
{
return viewport->getMaximumVisibleWidth();
}
int ListBox::getVisibleContentWidth() const noexcept { return viewport->getMaximumVisibleWidth(); }
ScrollBar* ListBox::getVerticalScrollBar() const noexcept
{
return viewport->getVerticalScrollBar();
}
ScrollBar* ListBox::getHorizontalScrollBar() const noexcept
{
return viewport->getHorizontalScrollBar();
}
ScrollBar* ListBox::getVerticalScrollBar() const noexcept { return viewport->getVerticalScrollBar(); }
ScrollBar* ListBox::getHorizontalScrollBar() const noexcept { return viewport->getHorizontalScrollBar(); }
void ListBox::colourChanged()
{
@ -894,39 +869,42 @@ void ListBox::repaintRow (const int rowNumber) noexcept
Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, int& imageY)
{
Rectangle<int> imageArea;
const int firstRow = getRowContainingPosition (0, viewport->getY());
auto firstRow = getRowContainingPosition (0, viewport->getY());
for (int i = getNumRowsOnScreen() + 2; --i >= 0;)
{
Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i);
if (rowComp != nullptr && rows.contains (firstRow + i))
if (rows.contains (firstRow + i))
{
const Point<int> pos (getLocalPoint (rowComp, Point<int>()));
const Rectangle<int> rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight());
imageArea = imageArea.getUnion (rowRect);
if (auto* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i))
{
auto pos = getLocalPoint (rowComp, Point<int>());
imageArea = imageArea.getUnion ({ pos.x, pos.y, rowComp->getWidth(), rowComp->getHeight() });
}
}
}
imageArea = imageArea.getIntersection (getLocalBounds());
imageX = imageArea.getX();
imageY = imageArea.getY();
Image snapshot (Image::ARGB, imageArea.getWidth(), imageArea.getHeight(), true);
for (int i = getNumRowsOnScreen() + 2; --i >= 0;)
{
Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i);
if (rowComp != nullptr && rows.contains (firstRow + i))
if (rows.contains (firstRow + i))
{
Graphics g (snapshot);
g.setOrigin (getLocalPoint (rowComp, Point<int>()) - imageArea.getPosition());
if (g.reduceClipRegion (rowComp->getLocalBounds()))
if (auto* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i))
{
g.beginTransparencyLayer (0.6f);
rowComp->paintEntireComponent (g, false);
g.endTransparencyLayer();
Graphics g (snapshot);
g.setOrigin (getLocalPoint (rowComp, Point<int>()) - imageArea.getPosition());
if (g.reduceClipRegion (rowComp->getLocalBounds()))
{
g.beginTransparencyLayer (0.6f);
rowComp->paintEntireComponent (g, false);
g.endTransparencyLayer();
}
}
}
}
@ -936,13 +914,12 @@ Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, in
void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet<int>& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows)
{
if (DragAndDropContainer* const dragContainer = DragAndDropContainer::findParentDragContainerFor (this))
if (auto* dragContainer = DragAndDropContainer::findParentDragContainerFor (this))
{
int x, y;
Image dragImage = createSnapshotOfRows (rowsToDrag, x, y);
auto dragImage = createSnapshotOfRows (rowsToDrag, x, y);
MouseEvent e2 (e.getEventRelativeTo (this));
const Point<int> p (x - e2.x, y - e2.y);
auto p = Point<int> (x, y) - e.getEventRelativeTo (this).position.toInt();
dragContainer->startDragging (dragDescription, this, dragImage, allowDraggingToOtherWindows, &p);
}
else

View file

@ -577,11 +577,11 @@ private:
ScopedPointer<ListViewport> viewport;
ScopedPointer<Component> headerComponent;
ScopedPointer<MouseListener> mouseMoveSelector;
int totalItems, rowHeight, minimumRowWidth;
int outlineThickness;
int lastRowSelected;
bool multipleSelection, alwaysFlipSelection, hasDoneInitialUpdate, selectOnMouseDown;
SparseSet<int> selected;
int totalItems = 0, rowHeight = 22, minimumRowWidth = 0;
int outlineThickness = 0;
int lastRowSelected = -1;
bool multipleSelection = false, alwaysFlipSelection = false, hasDoneInitialUpdate = false, selectOnMouseDown = true;
void selectRowInternal (int rowNumber, bool dontScrollToShowThisRow,
bool deselectOthersFirst, bool isMouseClick);