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:
parent
320c8002da
commit
6bb3d9b1db
2 changed files with 104 additions and 127 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue