123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962 |
- /*
- ==============================================================================
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
- JUCE is an open source library subject to commercial or open-source
- licensing.
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
- ==============================================================================
- */
- namespace juce
- {
- class ListBox::RowComponent : public Component,
- public TooltipClient
- {
- public:
- RowComponent (ListBox& lb) : owner (lb) {}
- void paint (Graphics& g) override
- {
- if (auto* m = owner.getModel())
- m->paintListBoxItem (row, g, getWidth(), getHeight(), selected);
- }
- void update (const int newRow, const bool nowSelected)
- {
- if (row != newRow || selected != nowSelected)
- {
- repaint();
- row = newRow;
- selected = nowSelected;
- }
- if (auto* m = owner.getModel())
- {
- setMouseCursor (m->getMouseCursorForRow (row));
- customComponent.reset (m->refreshComponentForRow (newRow, nowSelected, customComponent.release()));
- if (customComponent != nullptr)
- {
- addAndMakeVisible (customComponent.get());
- customComponent->setBounds (getLocalBounds());
- }
- }
- }
- 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 || isInDragToScrollViewport()))
- performSelection (e, false);
- else
- selectRowOnMouseUp = true;
- }
- }
- void mouseUp (const MouseEvent& e) override
- {
- if (isEnabled() && selectRowOnMouseUp && ! (isDragging || isDraggingToScroll))
- performSelection (e, true);
- }
- void mouseDoubleClick (const MouseEvent& e) override
- {
- if (isEnabled())
- if (auto* m = owner.getModel())
- m->listBoxItemDoubleClicked (row, e);
- }
- void mouseDrag (const MouseEvent& e) override
- {
- if (auto* m = owner.getModel())
- {
- if (isEnabled() && e.mouseWasDraggedSinceMouseDown() && ! isDragging)
- {
- SparseSet<int> rowsToDrag;
- if (owner.selectOnMouseDown || owner.isRowSelected (row))
- rowsToDrag = owner.getSelectedRows();
- else
- rowsToDrag.addRange (Range<int>::withStartAndLength (row, 1));
- if (rowsToDrag.size() > 0)
- {
- auto dragDescription = m->getDragSourceDescription (rowsToDrag);
- if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty())))
- {
- isDragging = true;
- owner.startDragAndDrop (e, rowsToDrag, dragDescription, true);
- }
- }
- }
- }
- if (! isDraggingToScroll)
- if (auto* vp = owner.getViewport())
- isDraggingToScroll = vp->isCurrentlyScrollingOnDrag();
- }
- void resized() override
- {
- if (customComponent != nullptr)
- customComponent->setBounds (getLocalBounds());
- }
- String getTooltip() override
- {
- if (auto* m = owner.getModel())
- return m->getTooltipForRow (row);
- return {};
- }
- ListBox& owner;
- std::unique_ptr<Component> customComponent;
- int row = -1;
- bool selected = false, isDragging = false, isDraggingToScroll = false, selectRowOnMouseUp = false;
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RowComponent)
- };
- //==============================================================================
- class ListBox::ListViewport : public Viewport
- {
- public:
- ListViewport (ListBox& lb) : owner (lb)
- {
- setWantsKeyboardFocus (false);
- auto content = new Component();
- setViewedComponent (content);
- content->setWantsKeyboardFocus (false);
- }
- RowComponent* getComponentForRow (const int row) const noexcept
- {
- return rows [row % jmax (1, rows.size())];
- }
- RowComponent* getComponentForRowIfOnscreen (const int row) const noexcept
- {
- return (row >= firstIndex && row < firstIndex + rows.size())
- ? getComponentForRow (row) : nullptr;
- }
- int getRowNumberOfComponent (Component* const rowComponent) const noexcept
- {
- const int index = getViewedComponent()->getIndexOfChildComponent (rowComponent);
- const int num = rows.size();
- for (int i = num; --i >= 0;)
- if (((firstIndex + i) % jmax (1, num)) == index)
- return firstIndex + i;
- return -1;
- }
- void visibleAreaChanged (const Rectangle<int>&) override
- {
- updateVisibleArea (true);
- if (auto* m = owner.getModel())
- m->listWasScrolled();
- }
- void updateVisibleArea (const bool makeSureItUpdatesContent)
- {
- hasUpdated = false;
- 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;
- content.setBounds (newX, newY, newW, newH);
- if (makeSureItUpdatesContent && ! hasUpdated)
- updateContents();
- }
- void updateContents()
- {
- hasUpdated = true;
- auto rowH = owner.getRowHeight();
- auto& content = *getViewedComponent();
- if (rowH > 0)
- {
- auto y = getViewPositionY();
- auto w = content.getWidth();
- const int numNeeded = 2 + getMaximumVisibleHeight() / rowH;
- rows.removeRange (numNeeded, rows.size());
- while (numNeeded > rows.size())
- {
- auto newRow = new RowComponent (owner);
- rows.add (newRow);
- content.addAndMakeVisible (newRow);
- }
- firstIndex = y / rowH;
- firstWholeIndex = (y + rowH - 1) / rowH;
- lastWholeIndex = (y + getMaximumVisibleHeight() - 1) / rowH;
- for (int i = 0; i < numNeeded; ++i)
- {
- const int row = i + firstIndex;
- if (auto* rowComp = getComponentForRow (row))
- {
- rowComp->setBounds (0, row * rowH, w, rowH);
- rowComp->update (row, owner.isRowSelected (row));
- }
- }
- }
- if (owner.headerComponent != nullptr)
- owner.headerComponent->setBounds (owner.outlineThickness + content.getX(),
- owner.outlineThickness,
- jmax (owner.getWidth() - owner.outlineThickness * 2,
- content.getWidth()),
- owner.headerComponent->getHeight());
- }
- void selectRow (const int row, const int rowH, const bool dontScroll,
- const int lastSelectedRow, const int totalRows, const bool isMouseClick)
- {
- hasUpdated = false;
- if (row < firstWholeIndex && ! dontScroll)
- {
- setViewPosition (getViewPositionX(), row * rowH);
- }
- else if (row >= lastWholeIndex && ! dontScroll)
- {
- const int rowsOnScreen = lastWholeIndex - firstWholeIndex;
- if (row >= lastSelectedRow + rowsOnScreen
- && rowsOnScreen < totalRows - 1
- && ! isMouseClick)
- {
- setViewPosition (getViewPositionX(),
- jlimit (0, jmax (0, totalRows - rowsOnScreen), row) * rowH);
- }
- else
- {
- setViewPosition (getViewPositionX(),
- jmax (0, (row + 1) * rowH - getMaximumVisibleHeight()));
- }
- }
- if (! hasUpdated)
- updateContents();
- }
- void scrollToEnsureRowIsOnscreen (const int row, const int rowH)
- {
- if (row < firstWholeIndex)
- {
- setViewPosition (getViewPositionX(), row * rowH);
- }
- else if (row >= lastWholeIndex)
- {
- setViewPosition (getViewPositionX(),
- jmax (0, (row + 1) * rowH - getMaximumVisibleHeight()));
- }
- }
- void paint (Graphics& g) override
- {
- if (isOpaque())
- g.fillAll (owner.findColour (ListBox::backgroundColourId));
- }
- bool keyPressed (const KeyPress& key) override
- {
- if (Viewport::respondsToKey (key))
- {
- const int allowableMods = owner.multipleSelection ? ModifierKeys::shiftModifier : 0;
- if ((key.getModifiers().getRawFlags() & ~allowableMods) == 0)
- {
- // we want to avoid these keypresses going to the viewport, and instead allow
- // them to pass up to our listbox..
- return false;
- }
- }
- return Viewport::keyPressed (key);
- }
- private:
- ListBox& owner;
- OwnedArray<RowComponent> rows;
- int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0;
- bool hasUpdated = false;
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListViewport)
- };
- //==============================================================================
- struct ListBoxMouseMoveSelector : public MouseListener
- {
- ListBoxMouseMoveSelector (ListBox& lb) : owner (lb)
- {
- owner.addMouseListener (this, true);
- }
- ~ListBoxMouseMoveSelector() override
- {
- owner.removeMouseListener (this);
- }
- void mouseMove (const MouseEvent& e) override
- {
- auto pos = e.getEventRelativeTo (&owner).position.toInt();
- owner.selectRow (owner.getRowContainingPosition (pos.x, pos.y), true);
- }
- void mouseExit (const MouseEvent& e) override
- {
- mouseMove (e);
- }
- ListBox& owner;
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ListBoxMouseMoveSelector)
- };
- //==============================================================================
- ListBox::ListBox (const String& name, ListBoxModel* const m)
- : Component (name), model (m)
- {
- viewport.reset (new ListViewport (*this));
- addAndMakeVisible (viewport.get());
- ListBox::setWantsKeyboardFocus (true);
- ListBox::colourChanged();
- }
- ListBox::~ListBox()
- {
- headerComponent.reset();
- viewport.reset();
- }
- void ListBox::setModel (ListBoxModel* const newModel)
- {
- if (model != newModel)
- {
- model = newModel;
- repaint();
- updateContent();
- }
- }
- void ListBox::setMultipleSelectionEnabled (bool b) noexcept { multipleSelection = b; }
- void ListBox::setClickingTogglesRowSelection (bool b) noexcept { alwaysFlipSelection = b; }
- void ListBox::setRowSelectedOnMouseDown (bool b) noexcept { selectOnMouseDown = b; }
- void ListBox::setMouseMoveSelectsRows (bool b)
- {
- if (b)
- {
- if (mouseMoveSelector == nullptr)
- mouseMoveSelector.reset (new ListBoxMouseMoveSelector (*this));
- }
- else
- {
- mouseMoveSelector.reset();
- }
- }
- //==============================================================================
- void ListBox::paint (Graphics& g)
- {
- if (! hasDoneInitialUpdate)
- updateContent();
- g.fillAll (findColour (backgroundColourId));
- }
- void ListBox::paintOverChildren (Graphics& g)
- {
- if (outlineThickness > 0)
- {
- g.setColour (findColour (outlineColourId));
- g.drawRect (getLocalBounds(), outlineThickness);
- }
- }
- void ListBox::resized()
- {
- viewport->setBoundsInset (BorderSize<int> (outlineThickness + (headerComponent != nullptr ? headerComponent->getHeight() : 0),
- outlineThickness, outlineThickness, outlineThickness));
- viewport->setSingleStepSizes (20, getRowHeight());
- viewport->updateVisibleArea (false);
- }
- void ListBox::visibilityChanged()
- {
- viewport->updateVisibleArea (true);
- }
- Viewport* ListBox::getViewport() const noexcept
- {
- return viewport.get();
- }
- //==============================================================================
- void ListBox::updateContent()
- {
- hasDoneInitialUpdate = true;
- totalItems = (model != nullptr) ? model->getNumRows() : 0;
- bool selectionChanged = false;
- if (selected.size() > 0 && selected [selected.size() - 1] >= totalItems)
- {
- selected.removeRange ({ totalItems, std::numeric_limits<int>::max() });
- lastRowSelected = getSelectedRow (0);
- selectionChanged = true;
- }
- viewport->updateVisibleArea (isVisible());
- viewport->resized();
- if (selectionChanged && model != nullptr)
- model->selectedRowsChanged (lastRowSelected);
- }
- //==============================================================================
- void ListBox::selectRow (int row, bool dontScroll, bool deselectOthersFirst)
- {
- selectRowInternal (row, dontScroll, deselectOthersFirst, false);
- }
- void ListBox::selectRowInternal (const int row,
- bool dontScroll,
- bool deselectOthersFirst,
- bool isMouseClick)
- {
- if (! multipleSelection)
- deselectOthersFirst = true;
- if ((! isRowSelected (row))
- || (deselectOthersFirst && getNumSelectedRows() > 1))
- {
- if (isPositiveAndBelow (row, totalItems))
- {
- if (deselectOthersFirst)
- selected.clear();
- selected.addRange ({ row, row + 1 });
- if (getHeight() == 0 || getWidth() == 0)
- dontScroll = true;
- viewport->selectRow (row, getRowHeight(), dontScroll,
- lastRowSelected, totalItems, isMouseClick);
- lastRowSelected = row;
- model->selectedRowsChanged (row);
- }
- else
- {
- if (deselectOthersFirst)
- deselectAllRows();
- }
- }
- }
- void ListBox::deselectRow (const int row)
- {
- if (selected.contains (row))
- {
- selected.removeRange ({ row, row + 1 });
- if (row == lastRowSelected)
- lastRowSelected = getSelectedRow (0);
- viewport->updateContents();
- model->selectedRowsChanged (lastRowSelected);
- }
- }
- void ListBox::setSelectedRows (const SparseSet<int>& setOfRowsToBeSelected,
- const NotificationType sendNotificationEventToModel)
- {
- selected = setOfRowsToBeSelected;
- selected.removeRange ({ totalItems, std::numeric_limits<int>::max() });
- if (! isRowSelected (lastRowSelected))
- lastRowSelected = getSelectedRow (0);
- viewport->updateContents();
- if (model != nullptr && sendNotificationEventToModel == sendNotification)
- model->selectedRowsChanged (lastRowSelected);
- }
- SparseSet<int> ListBox::getSelectedRows() const
- {
- return selected;
- }
- void ListBox::selectRangeOfRows (int firstRow, int lastRow, bool dontScrollToShowThisRange)
- {
- if (multipleSelection && (firstRow != lastRow))
- {
- const int numRows = totalItems - 1;
- firstRow = jlimit (0, jmax (0, numRows), firstRow);
- lastRow = jlimit (0, jmax (0, numRows), lastRow);
- selected.addRange ({ jmin (firstRow, lastRow),
- jmax (firstRow, lastRow) + 1 });
- selected.removeRange ({ lastRow, lastRow + 1 });
- }
- selectRowInternal (lastRow, dontScrollToShowThisRange, false, true);
- }
- void ListBox::flipRowSelection (const int row)
- {
- if (isRowSelected (row))
- deselectRow (row);
- else
- selectRowInternal (row, false, false, true);
- }
- void ListBox::deselectAllRows()
- {
- if (! selected.isEmpty())
- {
- selected.clear();
- lastRowSelected = -1;
- viewport->updateContents();
- if (model != nullptr)
- model->selectedRowsChanged (lastRowSelected);
- }
- }
- void ListBox::selectRowsBasedOnModifierKeys (const int row,
- ModifierKeys mods,
- const bool isMouseUpEvent)
- {
- if (multipleSelection && (mods.isCommandDown() || alwaysFlipSelection))
- {
- flipRowSelection (row);
- }
- else if (multipleSelection && mods.isShiftDown() && lastRowSelected >= 0)
- {
- selectRangeOfRows (lastRowSelected, row);
- }
- else if ((! mods.isPopupMenu()) || ! isRowSelected (row))
- {
- selectRowInternal (row, false, ! (multipleSelection && (! isMouseUpEvent) && isRowSelected (row)), true);
- }
- }
- int ListBox::getNumSelectedRows() const
- {
- return selected.size();
- }
- int ListBox::getSelectedRow (const int index) const
- {
- return (isPositiveAndBelow (index, selected.size()))
- ? selected [index] : -1;
- }
- bool ListBox::isRowSelected (const int row) const
- {
- return selected.contains (row);
- }
- int ListBox::getLastRowSelected() const
- {
- return isRowSelected (lastRowSelected) ? lastRowSelected : -1;
- }
- //==============================================================================
- int ListBox::getRowContainingPosition (const int x, const int y) const noexcept
- {
- if (isPositiveAndBelow (x, getWidth()))
- {
- const int row = (viewport->getViewPositionY() + y - viewport->getY()) / rowHeight;
- if (isPositiveAndBelow (row, totalItems))
- return row;
- }
- return -1;
- }
- int ListBox::getInsertionIndexForPosition (const int x, const int y) const noexcept
- {
- if (isPositiveAndBelow (x, getWidth()))
- return jlimit (0, totalItems, (viewport->getViewPositionY() + y + rowHeight / 2 - viewport->getY()) / rowHeight);
- return -1;
- }
- Component* ListBox::getComponentForRowNumber (const int row) const noexcept
- {
- if (auto* listRowComp = viewport->getComponentForRowIfOnscreen (row))
- return listRowComp->customComponent.get();
- return nullptr;
- }
- int ListBox::getRowNumberOfComponent (Component* const rowComponent) const noexcept
- {
- return viewport->getRowNumberOfComponent (rowComponent);
- }
- Rectangle<int> ListBox::getRowPosition (int rowNumber, bool relativeToComponentTopLeft) const noexcept
- {
- auto y = viewport->getY() + rowHeight * rowNumber;
- if (relativeToComponentTopLeft)
- y -= viewport->getViewPositionY();
- return { viewport->getX(), y,
- viewport->getViewedComponent()->getWidth(), rowHeight };
- }
- void ListBox::setVerticalPosition (const double proportion)
- {
- auto offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
- viewport->setViewPosition (viewport->getViewPositionX(),
- jmax (0, roundToInt (proportion * offscreen)));
- }
- double ListBox::getVerticalPosition() const
- {
- auto offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight();
- return offscreen > 0 ? viewport->getViewPositionY() / (double) offscreen
- : 0;
- }
- int ListBox::getVisibleRowWidth() const noexcept
- {
- return viewport->getViewWidth();
- }
- void ListBox::scrollToEnsureRowIsOnscreen (const int row)
- {
- viewport->scrollToEnsureRowIsOnscreen (row, getRowHeight());
- }
- //==============================================================================
- bool ListBox::keyPressed (const KeyPress& key)
- {
- const int numVisibleRows = viewport->getHeight() / getRowHeight();
- const bool multiple = multipleSelection
- && lastRowSelected >= 0
- && key.getModifiers().isShiftDown();
- if (key.isKeyCode (KeyPress::upKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, lastRowSelected - 1);
- else
- selectRow (jmax (0, lastRowSelected - 1));
- }
- else if (key.isKeyCode (KeyPress::downKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, lastRowSelected + 1);
- else
- selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected + 1)));
- }
- else if (key.isKeyCode (KeyPress::pageUpKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, lastRowSelected - numVisibleRows);
- else
- selectRow (jmax (0, jmax (0, lastRowSelected) - numVisibleRows));
- }
- else if (key.isKeyCode (KeyPress::pageDownKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, lastRowSelected + numVisibleRows);
- else
- selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected) + numVisibleRows));
- }
- else if (key.isKeyCode (KeyPress::homeKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, 0);
- else
- selectRow (0);
- }
- else if (key.isKeyCode (KeyPress::endKey))
- {
- if (multiple)
- selectRangeOfRows (lastRowSelected, totalItems - 1);
- else
- selectRow (totalItems - 1);
- }
- else if (key.isKeyCode (KeyPress::returnKey) && isRowSelected (lastRowSelected))
- {
- if (model != nullptr)
- model->returnKeyPressed (lastRowSelected);
- }
- else if ((key.isKeyCode (KeyPress::deleteKey) || key.isKeyCode (KeyPress::backspaceKey))
- && isRowSelected (lastRowSelected))
- {
- if (model != nullptr)
- model->deleteKeyPressed (lastRowSelected);
- }
- else if (multipleSelection && key == KeyPress ('a', ModifierKeys::commandModifier, 0))
- {
- selectRangeOfRows (0, std::numeric_limits<int>::max());
- }
- else
- {
- return false;
- }
- return true;
- }
- bool ListBox::keyStateChanged (const bool isKeyDown)
- {
- return isKeyDown
- && (KeyPress::isKeyCurrentlyDown (KeyPress::upKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::pageUpKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::downKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::pageDownKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::homeKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::endKey)
- || KeyPress::isKeyCurrentlyDown (KeyPress::returnKey));
- }
- void ListBox::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
- {
- bool eventWasUsed = false;
- if (wheel.deltaX != 0.0f && getHorizontalScrollBar().isVisible())
- {
- eventWasUsed = true;
- getHorizontalScrollBar().mouseWheelMove (e, wheel);
- }
- if (wheel.deltaY != 0.0f && getVerticalScrollBar().isVisible())
- {
- eventWasUsed = true;
- getVerticalScrollBar().mouseWheelMove (e, wheel);
- }
- if (! eventWasUsed)
- Component::mouseWheelMove (e, wheel);
- }
- void ListBox::mouseUp (const MouseEvent& e)
- {
- if (e.mouseWasClicked() && model != nullptr)
- model->backgroundClicked (e);
- }
- //==============================================================================
- void ListBox::setRowHeight (const int newHeight)
- {
- rowHeight = jmax (1, newHeight);
- viewport->setSingleStepSizes (20, rowHeight);
- updateContent();
- }
- int ListBox::getNumRowsOnScreen() const noexcept
- {
- return viewport->getMaximumVisibleHeight() / rowHeight;
- }
- void ListBox::setMinimumContentWidth (const int newMinimumWidth)
- {
- minimumRowWidth = newMinimumWidth;
- updateContent();
- }
- int ListBox::getVisibleContentWidth() const noexcept { return viewport->getMaximumVisibleWidth(); }
- ScrollBar& ListBox::getVerticalScrollBar() const noexcept { return viewport->getVerticalScrollBar(); }
- ScrollBar& ListBox::getHorizontalScrollBar() const noexcept { return viewport->getHorizontalScrollBar(); }
- void ListBox::colourChanged()
- {
- setOpaque (findColour (backgroundColourId).isOpaque());
- viewport->setOpaque (isOpaque());
- repaint();
- }
- void ListBox::parentHierarchyChanged()
- {
- colourChanged();
- }
- void ListBox::setOutlineThickness (int newThickness)
- {
- outlineThickness = newThickness;
- resized();
- }
- void ListBox::setHeaderComponent (Component* newHeaderComponent)
- {
- if (headerComponent.get() != newHeaderComponent)
- {
- headerComponent.reset (newHeaderComponent);
- addAndMakeVisible (newHeaderComponent);
- ListBox::resized();
- }
- }
- void ListBox::repaintRow (const int rowNumber) noexcept
- {
- repaint (getRowPosition (rowNumber, true));
- }
- Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, int& imageY)
- {
- Rectangle<int> imageArea;
- auto firstRow = getRowContainingPosition (0, viewport->getY());
- for (int i = getNumRowsOnScreen() + 2; --i >= 0;)
- {
- if (rows.contains (firstRow + i))
- {
- 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();
- auto listScale = Component::getApproximateScaleFactorForComponent (this);
- Image snapshot (Image::ARGB, roundToInt (imageArea.getWidth() * listScale), roundToInt (imageArea.getHeight() * listScale), true);
- for (int i = getNumRowsOnScreen() + 2; --i >= 0;)
- {
- if (rows.contains (firstRow + i))
- {
- if (auto* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i))
- {
- Graphics g (snapshot);
- g.setOrigin (getLocalPoint (rowComp, Point<int>()) - imageArea.getPosition());
- auto rowScale = Component::getApproximateScaleFactorForComponent (rowComp);
- if (g.reduceClipRegion (rowComp->getLocalBounds() * rowScale))
- {
- g.beginTransparencyLayer (0.6f);
- g.addTransform (AffineTransform::scale (rowScale));
- rowComp->paintEntireComponent (g, false);
- g.endTransparencyLayer();
- }
- }
- }
- }
- return snapshot;
- }
- void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet<int>& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows)
- {
- if (auto* dragContainer = DragAndDropContainer::findParentDragContainerFor (this))
- {
- int x, y;
- auto dragImage = createSnapshotOfRows (rowsToDrag, x, y);
- auto p = Point<int> (x, y) - e.getEventRelativeTo (this).position.toInt();
- dragContainer->startDragging (dragDescription, this, dragImage, allowDraggingToOtherWindows, &p, &e.source);
- }
- else
- {
- // to be able to do a drag-and-drop operation, the listbox needs to
- // be inside a component which is also a DragAndDropContainer.
- jassertfalse;
- }
- }
- //==============================================================================
- Component* ListBoxModel::refreshComponentForRow (int, bool, Component* existingComponentToUpdate)
- {
- ignoreUnused (existingComponentToUpdate);
- jassert (existingComponentToUpdate == nullptr); // indicates a failure in the code that recycles the components
- return nullptr;
- }
- void ListBoxModel::listBoxItemClicked (int, const MouseEvent&) {}
- void ListBoxModel::listBoxItemDoubleClicked (int, const MouseEvent&) {}
- void ListBoxModel::backgroundClicked (const MouseEvent&) {}
- void ListBoxModel::selectedRowsChanged (int) {}
- void ListBoxModel::deleteKeyPressed (int) {}
- void ListBoxModel::returnKeyPressed (int) {}
- void ListBoxModel::listWasScrolled() {}
- var ListBoxModel::getDragSourceDescription (const SparseSet<int>&) { return {}; }
- String ListBoxModel::getTooltipForRow (int) { return {}; }
- MouseCursor ListBoxModel::getMouseCursorForRow (int) { return MouseCursor::NormalCursor; }
- } // namespace juce
|