* Multiselect listbox is functional, few functions are missing. 4.x-dev

Mon, 26 Oct 2020 22:27:52 +0200

author
cemkalyoncu
date
Mon, 26 Oct 2020 22:27:52 +0200
branch
4.x-dev
changeset 1470
762bf25977fb
parent 1469
5c0962155ba0
child 1471
42787ccaa5b8

* Multiselect listbox is functional, few functions are missing.

Source/Gorgon/Widgets/Listbox.h file | annotate | diff | comparison | revisions
Testing/Source/Manual/UI_Generate.cpp file | annotate | diff | comparison | revisions
--- a/Source/Gorgon/Widgets/Listbox.h	Wed Oct 21 09:29:25 2020 +0300
+++ b/Source/Gorgon/Widgets/Listbox.h	Mon Oct 26 22:27:52 2020 +0200
@@ -379,8 +379,6 @@
             void sel_destroy(W_ &w) {
                 if(&w == selected) {
                     selected = nullptr;
-                    selectedindex = -1;
-                    ChangedEvent(-1);
                 }
                 if(w.IsFocused()) {
                     focusindex = -1;
@@ -407,26 +405,121 @@
         //follow the focus by default. If desired, this can be changed
         template<class T_, class W_, class F_>
         class LBSELTR_Multi {
+            class SelectionIndexHelper {
+            public:
+                SelectionIndexHelper(std::vector<long> &v) : v(v) {
+                }
+                
+                auto begin() {
+                    return v.begin();
+                }
+                
+                auto begin() const {
+                    return v.begin();
+                }
+                
+                auto end() {
+                    return v.end();
+                }
+                
+                auto end() const {
+                    return v.end();
+                }
+                
+            private:
+                std::vector<long> &v;
+            };
         public:
             
+            /// Removes all items from the selection
+            void ClearSelection() {
+                selected.clear();
+                dynamic_cast<F_*>(this)->Refresh();
+            }
+            
+            /// Replaces the selection by the given index
+            void SetSelection(long ind) {
+                SetSelection(std::vector<long>{ind});
+            };
+            
+            /// Replaces the selection by the given indices
+            void SetSelection(std::vector<long> indices) {
+                selected = std::move(indices);
+                std::sort(selected.begin(), selected.end());
+                
+                //ensure nothing is duplicated
+                selected.erase(std::unique(selected.begin(), selected.end()), selected.end());
+                
+                dynamic_cast<F_*>(this)->Refresh();
+            };
+            
+            /// Replaces the selection by the given indices
+            template<class ...P_>
+            void SetSelection(P_&&... elms) {
+                SetSelection({elms...});
+            }
+            
+            /// Adds the given index to the selection
+            void AddToSelection(long ind) {
+                AddToSelection(std::vector<long>{ind});
+            };
+            
+            /// Adds the given indices to the selection
+            void AddToSelection(std::vector<long> indices) {
+                std::sort(indices.begin(), indices.end());
+                
+                selected.resize(selected.size() + indices.size());
+                auto sel = selected.rbegin();
+                auto cur = selected.rbegin() + indices.size();
+                auto ind = indices.rbegin();
+                
+                while(ind != indices.rend()) {
+                    if(cur == selected.rend() || *cur < *ind) {
+                        *sel = *ind;
+                        ind++;
+                    }
+                    else {
+                        *sel = *cur;
+                        cur++;
+                    }
+                    sel++;
+                }
+                
+                //ensure nothing is duplicated
+                selected.erase(std::unique(selected.begin(), selected.end()), selected.end());
+                
+                dynamic_cast<F_*>(this)->Refresh();
+            };
+            
+            /// Adds the given indices to the selection
+            template<class ...P_>
+            void AddToSelection(P_&&... elms) {
+                AddToSelection({elms...});
+            }
+            
+            /// Returns if the item in the given index is selected.
+            bool IsSelected(long index) const {
+                return std::binary_search(selected.begin(), selected.end(), index);
+            }
+            
+            /// Returns how many items are selected
+            long GetSelectionCount() const {
+                return long(selected.size());
+            }
+            
             /*
-            ClearSelection
-            SetSelection(...)
-            AddToSelection(...)
-            IsSelected
-            GetSelectionCount
             Selection -> subclass allows for(auto item : list.Selection)
-            SelectedIndexes -> subclass allows for(auto index : list.SelectedIndexes)
             RemoveFromSelection
             SelectionStrategy(Click toggles/Ctrl select)
             SelectAll
+            Invert
             */
+            SelectionIndexHelper SelectedIndices;
             
-            
-            Event<LBSELTR_Multi, long> ChangedEvent = Event<LBSELTR_Multi, long, bool>{this};
+            Event<LBSELTR_Multi, long, bool> ChangedEvent = Event<LBSELTR_Multi, long, bool>{this};
             
         protected:
-            LBSELTR_Multi() {
+            LBSELTR_Multi() : SelectedIndices(selected) {
             }
             
             virtual ~LBSELTR_Multi() { }
@@ -444,17 +537,20 @@
             }
             
             void sel_toggled(long index, W_ &w) {
-                /*if(selectedindex == index || focusonly)
-                    return;
-                
-                if(selected)
-                    selected->SetSelected(false);
+                auto item = std::lower_bound(selected.begin(), selected.end(), index);
                 
-                w.SetSelected(true);
-                selectedindex = index;
-                selected = &w;
-                
-                ChangedEvent(index);*/
+                if(item != selected.end() && *item == index) {
+                    w.SetSelected(false);
+                    selected.erase(item);
+                    
+                    ChangedEvent(index, false);
+                }
+                else {
+                    w.SetSelected(true);
+                    selected.insert(item, index);
+                    
+                    ChangedEvent(index, true);
+                }
             }
             
             void sel_apply(long index, W_ &w, const T_ &) {
@@ -465,16 +561,12 @@
                     w.Defocus();
                 }
                 
-                /*if(index == selectedindex) {
+                if(std::binary_search(selected.begin(), selected.end(), index)) {
                     w.SetSelected(true);
-                    selected = &w;
                 }
                 else {
                     w.SetSelected(false);
-                    
-                    if(&w == selected)
-                        selected = nullptr;
-                }*/
+                }
             }
             
             void sel_prepare(W_ &w) {
@@ -490,10 +582,15 @@
                 if(index <= focusindex)
                     focusindex += count;
                 
-                //update selection
+                auto item = std::lower_bound(selected.begin(), selected.end(), index);
+                for(; item != selected.end(); ++item) {
+                    *item += count;
+                }
             }
             
             void sel_move(long index, long target) { 
+                if(index == target) 
+                    return;
                 //move triggers apply to both indexes
                 
                 if(index == focusindex) {
@@ -506,7 +603,46 @@
                     focusindex--;
                 }
                 
-                //update selection
+                if(index < target) {
+                    auto from = std::lower_bound(selected.begin(), selected.end(), index);
+                    auto to = std::lower_bound(selected.begin(), selected.end(), target+1);
+                
+                    auto shifted = from;
+                    
+                    if(*from == index)
+                        shifted++;
+                    
+                    while(from < to) {
+                        *from = *shifted - 1;
+                        
+                        from++;
+                        shifted++;
+                    }
+                    
+                    if(shifted != from) {
+                        *shifted = target;
+                    }
+                }
+                else {
+                    auto from = std::lower_bound(selected.begin(), selected.end(), index-1);
+                    auto to = std::upper_bound(selected.begin(), selected.end(), target);
+                
+                    auto shifted = from;
+                    
+                    if(*from == index)
+                        shifted--;
+                    
+                    while(from > to) {
+                        *from = *shifted + 1;
+                        
+                        from--;
+                        shifted--;
+                    }
+                    
+                    if(shifted != from) {
+                        *shifted = target;
+                    }
+                }
             }
             
             void sel_remove(long index, long count) { 
@@ -514,19 +650,32 @@
                 if(index <= focusindex) {
                     if(index+count > focusindex) {
                         focusindex = -1;
-                        ChangedEvent(-1);
                     }
                     else {
                         focusindex -= count;
                     }
                 }
                 
-                //update selection
+                //move and update items that are after the point of removal
+                auto item = std::lower_bound(selected.begin(), selected.end(), index);
+                auto shifted = item;
+                while(shifted != selected.end()) {
+                    if(*item < index+count) {
+                        shifted++;
+                    }
+                    
+                    *item = *shifted - count;
+                    
+                    shifted++;
+                    item++;
+                }
+                
+                //remove the items to be removed, they are already moved to the end
+                if(item != selected.end())
+                    selected.erase(item, selected.end());
             }
             
             void sel_destroy(W_ &w) {
-                //update selection
-                
                 if(w.IsFocused()) {
                     focusindex = -1;
                     w.RemoveFocus();
@@ -542,7 +691,7 @@
             }
             
             long focusindex = -1;
-            std::set<long> selected;
+            std::vector<long> selected; //this list will be kept sorted
         };
         
         template<class T_, class W_, class S_, class F_>
@@ -617,6 +766,15 @@
                 dynamic_cast<F_*>(this)->Refresh();
             }
             
+            /// Return the index of the first item that has the given value.
+            /// Returns -1 if item not found.
+            long Find(const T_ item, long start = 0) {
+                auto it = std::find(storage.begin() + start, storage.end(), item);
+                if(it == storage.end())
+                    return -1;
+                else
+                    return it-storage.begin();
+            }
             
             /**
              * @name Iteration 
@@ -1168,5 +1326,49 @@
         using internal::LBSTR_STLVector<T_, ListItem, std::vector<T_>, SimpleListbox<T_>>::Remove;
     };
     
+    /**
+     * This is a simple multi select listbox. It does not store any additional 
+     * information about each item, therefore, can store large amounts of items. 
+     * Items are stored in a std::vector.
+     */
+    template<class T_>
+    class MultiListbox : 
+        public ListboxBase<T_, ListItem, 
+            internal::LBTR_blank <T_, ListItem>,
+            internal::LBTRF_blank<T_, ListItem, MultiListbox<T_>>,
+            internal::LBSTR_STLVector<T_, ListItem, std::vector<T_>, MultiListbox<T_>>,
+            internal::LBSELTR_Multi<T_, ListItem, MultiListbox<T_>>
+        >
+    {
+        using Base = ListboxBase<T_, ListItem, 
+            internal::LBTR_blank <T_, ListItem>,
+            internal::LBTRF_blank<T_, ListItem, MultiListbox<T_>>,
+            internal::LBSTR_STLVector<T_, ListItem, std::vector<T_>, MultiListbox<T_>>,
+            internal::LBSELTR_Multi<T_, ListItem, MultiListbox<T_>>
+        >;
+        
+        friend internal::LBTR_blank <T_, ListItem>;
+        friend internal::LBTRF_blank<T_, ListItem, MultiListbox<T_>>;
+        friend internal::LBSTR_STLVector<T_, ListItem, std::vector<T_>, MultiListbox<T_>>;
+        friend internal::LBSELTR_Multi<T_, ListItem, MultiListbox<T_>>;
+        
+    public:
+        explicit MultiListbox(Registry::TemplateType temp = Registry::Listbox_Regular) : Base(Registry::Active()[temp]) {
+        }
+        
+        virtual bool Activate() {
+            return false;
+        }
+        
+        MultiListbox &operator =(const T_ value) {
+            this->SetSelection(value);
+            
+            return *this;
+        }
+        
+        using Base::ComponentStackWidget::Widget::Remove;
+        using internal::LBSTR_STLVector<T_, ListItem, std::vector<T_>, MultiListbox<T_>>::Remove;
+    };
+    
 } }
 
--- a/Testing/Source/Manual/UI_Generate.cpp	Wed Oct 21 09:29:25 2020 +0300
+++ b/Testing/Source/Manual/UI_Generate.cpp	Mon Oct 26 22:27:52 2020 +0200
@@ -165,16 +165,18 @@
         std::cout << "size f changed " << sizef.GetText();
     });
     
-    Widgets::SimpleListbox<std::string> list;
+    Widgets::MultiListbox<std::string> list;
     list.Add("5");
     list.Add("Âj");
     list.Add("9");
     list.Add("Hello", "World", "You", "are", "welcome", "to", "use", "the", "Gorgon", "Library");
-    list.SetSelectedIndex(3);
+    list.AddToSelection(3);
+    list.AddToSelection(1, 4);
     list.Insert(2, "!");
+    list.Remove(2);
     list.MoveBefore(2, 5);
     list.SetOverscroll(2);
-    list.ChangedEvent.Register([&list](long index) {
+    /*list.ChangedEvent.Register([&list](long index) {
         if(index == -1)
             return;
         
@@ -183,7 +185,7 @@
             return;
         
         list.Remove(index - 1);
-    });
+    });*/
     
     app.wind.Add(blank);
     addme(blank, btn);

mercurial