1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 29 //============================================================================== 30 /** 31 A class used by the LassoComponent to manage the things that it selects. 32 33 This allows the LassoComponent to find out which items are within the lasso, 34 and to change the list of selected items. 35 36 @see LassoComponent, SelectedItemSet 37 38 @tags{GUI} 39 */ 40 template <class SelectableItemType> 41 class LassoSource 42 { 43 public: 44 /** Destructor. */ 45 virtual ~LassoSource() = default; 46 47 /** Returns the set of items that lie within a given lassoable region. 48 49 Your implementation of this method must find all the relevant items that lie 50 within the given rectangle. and add them to the itemsFound array. 51 52 The coordinates are relative to the top-left of the lasso component's parent 53 component. (i.e. they are the same as the size and position of the lasso 54 component itself). 55 */ 56 virtual void findLassoItemsInArea (Array<SelectableItemType>& itemsFound, 57 const Rectangle<int>& area) = 0; 58 59 /** Returns the SelectedItemSet that the lasso should update. 60 61 This set will be continuously updated by the LassoComponent as it gets 62 dragged around, so make sure that you've got a ChangeListener attached to 63 the set so that your UI objects will know when the selection changes and 64 be able to update themselves appropriately. 65 */ 66 virtual SelectedItemSet<SelectableItemType>& getLassoSelection() = 0; 67 }; 68 69 70 //============================================================================== 71 /** 72 A component that acts as a rectangular selection region, which you drag with 73 the mouse to select groups of objects (in conjunction with a SelectedItemSet). 74 75 To use one of these: 76 77 - In your mouseDown or mouseDrag event, add the LassoComponent to your parent 78 component, and call its beginLasso() method, giving it a 79 suitable LassoSource object that it can use to find out which items are in 80 the active area. 81 82 - Each time your parent component gets a mouseDrag event, call dragLasso() 83 to update the lasso's position - it will use its LassoSource to calculate and 84 update the current selection. 85 86 - After the drag has finished and you get a mouseUp callback, you should call 87 endLasso() to clean up. This will make the lasso component invisible, and you 88 can remove it from the parent component, or delete it. 89 90 The class takes into account the modifier keys that are being held down while 91 the lasso is being dragged, so if shift is pressed, then any lassoed items will 92 be added to the original selection; if ctrl or command is pressed, they will be 93 xor'ed with any previously selected items. 94 95 @see LassoSource, SelectedItemSet 96 97 @tags{GUI} 98 */ 99 template <class SelectableItemType> 100 class LassoComponent : public Component 101 { 102 public: 103 //============================================================================== 104 /** Creates a Lasso component. */ 105 LassoComponent() = default; 106 107 //============================================================================== 108 /** Call this in your mouseDown event, to initialise a drag. 109 110 Pass in a suitable LassoSource object which the lasso will use to find 111 the items and change the selection. 112 113 After using this method to initialise the lasso, repeatedly call dragLasso() 114 in your component's mouseDrag callback. 115 116 @see dragLasso, endLasso, LassoSource 117 */ beginLasso(const MouseEvent & e,LassoSource<SelectableItemType> * lassoSource)118 void beginLasso (const MouseEvent& e, LassoSource<SelectableItemType>* lassoSource) 119 { 120 jassert (source == nullptr); // this suggests that you didn't call endLasso() after the last drag... 121 jassert (lassoSource != nullptr); // the source can't be null! 122 jassert (getParentComponent() != nullptr); // you need to add this to a parent component for it to work! 123 124 source = lassoSource; 125 126 if (lassoSource != nullptr) 127 originalSelection = lassoSource->getLassoSelection().getItemArray(); 128 129 setSize (0, 0); 130 dragStartPos = e.getMouseDownPosition(); 131 } 132 133 /** Call this in your mouseDrag event, to update the lasso's position. 134 135 This must be repeatedly calling when the mouse is dragged, after you've 136 first initialised the lasso with beginLasso(). 137 138 This method takes into account the modifier keys that are being held down, so 139 if shift is pressed, then the lassoed items will be added to any that were 140 previously selected; if ctrl or command is pressed, then they will be xor'ed 141 with previously selected items. 142 143 @see beginLasso, endLasso 144 */ dragLasso(const MouseEvent & e)145 void dragLasso (const MouseEvent& e) 146 { 147 if (source != nullptr) 148 { 149 setBounds (Rectangle<int> (dragStartPos, e.getPosition())); 150 setVisible (true); 151 152 Array<SelectableItemType> itemsInLasso; 153 source->findLassoItemsInArea (itemsInLasso, getBounds()); 154 155 if (e.mods.isShiftDown()) 156 { 157 itemsInLasso.removeValuesIn (originalSelection); // to avoid duplicates 158 itemsInLasso.addArray (originalSelection); 159 } 160 else if (e.mods.isCommandDown() || e.mods.isAltDown()) 161 { 162 auto originalMinusNew = originalSelection; 163 originalMinusNew.removeValuesIn (itemsInLasso); 164 165 itemsInLasso.removeValuesIn (originalSelection); 166 itemsInLasso.addArray (originalMinusNew); 167 } 168 169 source->getLassoSelection() = SelectedItemSet<SelectableItemType> (itemsInLasso); 170 } 171 } 172 173 /** Call this in your mouseUp event, after the lasso has been dragged. 174 @see beginLasso, dragLasso 175 */ endLasso()176 void endLasso() 177 { 178 source = nullptr; 179 originalSelection.clear(); 180 setVisible (false); 181 } 182 183 //============================================================================== 184 /** A set of colour IDs to use to change the colour of various aspects of the label. 185 186 These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() 187 methods. 188 189 Note that you can also use the constants from TextEditor::ColourIds to change the 190 colour of the text editor that is opened when a label is editable. 191 192 @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour 193 */ 194 enum ColourIds 195 { 196 lassoFillColourId = 0x1000440, /**< The colour to fill the lasso rectangle with. */ 197 lassoOutlineColourId = 0x1000441, /**< The colour to draw the outline with. */ 198 }; 199 200 //============================================================================== 201 /** @internal */ paint(Graphics & g)202 void paint (Graphics& g) override 203 { 204 getLookAndFeel().drawLasso (g, *this); 205 206 // this suggests that you've left a lasso comp lying around after the 207 // mouse drag has finished.. Be careful to call endLasso() when you get a 208 // mouse-up event. 209 jassert (isMouseButtonDownAnywhere()); 210 } 211 212 /** @internal */ hitTest(int,int)213 bool hitTest (int, int) override { return false; } 214 215 private: 216 //============================================================================== 217 Array<SelectableItemType> originalSelection; 218 LassoSource<SelectableItemType>* source = nullptr; 219 Point<int> dragStartPos; 220 221 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LassoComponent) 222 }; 223 224 } // namespace juce 225