Author Topic: ScrollList with Grid InsertAtIndex(), getItemAtIndex()  (Read 15387 times)

wallabie

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 200
    • View Profile
ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« on: April 10, 2014, 07:27:29 AM »
1. Got a ScrollList with a Grid, Alphabet, Positional ordering.
2. How to do an InsertAtIndex() or getItemAtIndex()

Another separate question:
3. In noticed that the grid has a custom ordering, is there an example showing how to use this feature?
 

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #1 on: April 13, 2014, 02:43:34 AM »
Here, try this UIGrid:
  1. //----------------------------------------------
  2. //            NGUI: Next-Gen UI kit
  3. // Copyright © 2011-2014 Tasharen Entertainment
  4. //----------------------------------------------
  5.  
  6. using UnityEngine;
  7. using System.Collections.Generic;
  8.  
  9. /// <summary>
  10. /// All children added to the game object with this script will be repositioned to be on a grid of specified dimensions.
  11. /// If you want the cells to automatically set their scale based on the dimensions of their content, take a look at UITable.
  12. /// </summary>
  13.  
  14. [AddComponentMenu("NGUI/Interaction/Grid")]
  15. public class UIGrid : UIWidgetContainer
  16. {
  17.         public delegate void OnReposition ();
  18.  
  19.         public enum Arrangement
  20.         {
  21.                 Horizontal,
  22.                 Vertical,
  23.         }
  24.  
  25.         public enum Sorting
  26.         {
  27.                 None,
  28.                 Alphabetic,
  29.                 Horizontal,
  30.                 Vertical,
  31.                 Custom,
  32.         }
  33.  
  34.         /// <summary>
  35.         /// Type of arrangement -- vertical or horizontal.
  36.         /// </summary>
  37.  
  38.         public Arrangement arrangement = Arrangement.Horizontal;
  39.  
  40.         /// <summary>
  41.         /// How to sort the grid's elements.
  42.         /// </summary>
  43.  
  44.         public Sorting sorting = Sorting.None;
  45.  
  46.         /// <summary>
  47.         /// Final pivot point for the grid's content.
  48.         /// </summary>
  49.  
  50.         public UIWidget.Pivot pivot = UIWidget.Pivot.TopLeft;
  51.  
  52.         /// <summary>
  53.         /// Maximum children per line.
  54.         /// If the arrangement is horizontal, this denotes the number of columns.
  55.         /// If the arrangement is vertical, this stands for the number of rows.
  56.         /// </summary>
  57.  
  58.         public int maxPerLine = 0;
  59.  
  60.         /// <summary>
  61.         /// The width of each of the cells.
  62.         /// </summary>
  63.  
  64.         public float cellWidth = 200f;
  65.  
  66.         /// <summary>
  67.         /// The height of each of the cells.
  68.         /// </summary>
  69.  
  70.         public float cellHeight = 200f;
  71.  
  72.         /// <summary>
  73.         /// Whether the grid will smoothly animate its children into the correct place.
  74.         /// </summary>
  75.  
  76.         public bool animateSmoothly = false;
  77.  
  78.         /// <summary>
  79.         /// Whether to ignore the disabled children or to treat them as being present.
  80.         /// </summary>
  81.  
  82.         public bool hideInactive = true;
  83.  
  84.         /// <summary>
  85.         /// Whether the parent container will be notified of the grid's changes.
  86.         /// </summary>
  87.  
  88.         public bool keepWithinPanel = false;
  89.  
  90.         /// <summary>
  91.         /// Callback triggered when the grid repositions its contents.
  92.         /// </summary>
  93.  
  94.         public OnReposition onReposition;
  95.  
  96.         /// <summary>
  97.         /// Custom sort delegate, used when the sorting method is set to 'custom'.
  98.         /// </summary>
  99.  
  100.         public BetterList<Transform>.CompareFunc onCustomSort;
  101.  
  102.         protected bool mReposition = false;
  103.         protected UIPanel mPanel;
  104.         protected bool mInitDone = false;
  105.  
  106.         /// <summary>
  107.         /// Reposition the children on the next Update().
  108.         /// </summary>
  109.  
  110.         public bool repositionNow { set { if (value) { mReposition = true; enabled = true; } } }
  111.  
  112.         /// <summary>
  113.         /// Get the current list of the grid's children.
  114.         /// </summary>
  115.  
  116.         public BetterList<Transform> GetChildList()
  117.         {
  118.                 Transform myTrans = transform;
  119.                 BetterList<Transform> list = new BetterList<Transform>();
  120.  
  121.                 for (int i = 0; i < myTrans.childCount; ++i)
  122.                 {
  123.                         Transform t = myTrans.GetChild(i);
  124.                         if (!hideInactive || (t && NGUITools.GetActive(t.gameObject)))
  125.                                 list.Add(t);
  126.                 }
  127.                 return list;
  128.         }
  129.  
  130.         /// <summary>
  131.         /// Convenience method: get the child at the specified index.
  132.         /// Note that if you plan on calling this function more than once, it's faster to get the entire list using GetChildList() instead.
  133.         /// </summary>
  134.  
  135.         public Transform GetChild (int index)
  136.         {
  137.                 BetterList<Transform> list = GetChildList();
  138.                 return (index < list.size) ? list[index] : null;
  139.         }
  140.  
  141.         /// <summary>
  142.         /// Convenience method -- add a new child.
  143.         /// </summary>
  144.  
  145.         public void AddChild (Transform trans) { AddChild(trans, true); }
  146.  
  147.         /// <summary>
  148.         /// Convenience method -- add a new child.
  149.         /// Note that if you plan on adding multiple objects, it's faster to GetChildList() and modify that instead.
  150.         /// </summary>
  151.  
  152.         public void AddChild (Transform trans, bool sort)
  153.         {
  154.                 if (trans != null)
  155.                 {
  156.                         BetterList<Transform> list = GetChildList();
  157.                         list.Add(trans);
  158.                         ResetPosition(list);
  159.                 }
  160.         }
  161.  
  162.         /// <summary>
  163.         /// Convenience method -- add a new child at the specified index.
  164.         /// Note that if you plan on adding multiple objects, it's faster to GetChildList() and modify that instead.
  165.         /// </summary>
  166.  
  167.         public void AddChild (Transform trans, int index)
  168.         {
  169.                 if (trans != null)
  170.                 {
  171.                         if (sorting != Sorting.None)
  172.                                 Debug.LogWarning("The Grid has sorting enabled, so AddChild at index may not work as expected.", this);
  173.  
  174.                         BetterList<Transform> list = GetChildList();
  175.                         list.Insert(index, trans);
  176.                         ResetPosition(list);
  177.                 }
  178.         }
  179.  
  180.         /// <summary>
  181.         /// Convenience method -- remove a child at the specified index.
  182.         /// Note that if you plan on removing multiple objects, it's faster to GetChildList() and modify that instead.
  183.         /// </summary>
  184.  
  185.         public Transform RemoveChild (int index)
  186.         {
  187.                 BetterList<Transform> list = GetChildList();
  188.  
  189.                 if (index < list.size)
  190.                 {
  191.                         Transform t = list[index];
  192.                         list.RemoveAt(index);
  193.                         ResetPosition(list);
  194.                         return t;
  195.                 }
  196.                 return null;
  197.         }
  198.  
  199.         /// <summary>
  200.         /// Remove the specified child from the list.
  201.         /// Note that if you plan on removing multiple objects, it's faster to GetChildList() and modify that instead.
  202.         /// </summary>
  203.  
  204.         public bool RemoveChild (Transform t)
  205.         {
  206.                 BetterList<Transform> list = GetChildList();
  207.  
  208.                 if (list.Remove(t))
  209.                 {
  210.                         ResetPosition(list);
  211.                         return true;
  212.                 }
  213.                 return false;
  214.         }
  215.  
  216.         /// <summary>
  217.         /// Initialize the grid. Executed only once.
  218.         /// </summary>
  219.  
  220.         protected virtual void Init ()
  221.         {
  222.                 mInitDone = true;
  223.                 mPanel = NGUITools.FindInParents<UIPanel>(gameObject);
  224.         }
  225.  
  226.         /// <summary>
  227.         /// Cache everything and reset the initial position of all children.
  228.         /// </summary>
  229.  
  230.         protected virtual void Start ()
  231.         {
  232.                 if (!mInitDone) Init();
  233.                 bool smooth = animateSmoothly;
  234.                 animateSmoothly = false;
  235.                 Reposition();
  236.                 animateSmoothly = smooth;
  237.                 enabled = false;
  238.         }
  239.  
  240.         /// <summary>
  241.         /// Reset the position if necessary, then disable the component.
  242.         /// </summary>
  243.  
  244.         protected virtual void Update ()
  245.         {
  246.                 if (mReposition) Reposition();
  247.                 enabled = false;
  248.         }
  249.  
  250.         // Various generic sorting functions
  251.         static public int SortByName (Transform a, Transform b) { return string.Compare(a.name, b.name); }
  252.         static public int SortHorizontal (Transform a, Transform b) { return a.localPosition.x.CompareTo(b.localPosition.x); }
  253.         static public int SortVertical (Transform a, Transform b) { return b.localPosition.y.CompareTo(a.localPosition.y); }
  254.  
  255.         /// <summary>
  256.         /// You can override this function, but in most cases it's easier to just set the onCustomSort delegate instead.
  257.         /// </summary>
  258.  
  259.         protected virtual void Sort (BetterList<Transform> list) { }
  260.  
  261.         /// <summary>
  262.         /// Recalculate the position of all elements within the grid, sorting them alphabetically if necessary.
  263.         /// </summary>
  264.  
  265.         [ContextMenu("Execute")]
  266.         public virtual void Reposition ()
  267.         {
  268.                 if (Application.isPlaying && !mInitDone && NGUITools.GetActive(this))
  269.                 {
  270.                         mReposition = true;
  271.                         return;
  272.                 }
  273.  
  274.                 if (!mInitDone) Init();
  275.  
  276.                 // Get the list of children in their current order
  277.                 BetterList<Transform> list = GetChildList();
  278.  
  279.                 // Sort the list using the desired sorting logic
  280.                 if (sorting != Sorting.None)
  281.                 {
  282.                         if (sorting == Sorting.Alphabetic) list.Sort(SortByName);
  283.                         else if (sorting == Sorting.Horizontal) list.Sort(SortHorizontal);
  284.                         else if (sorting == Sorting.Vertical) list.Sort(SortVertical);
  285.                         else if (onCustomSort != null) list.Sort(onCustomSort);
  286.                         else Sort(list);
  287.                 }
  288.  
  289.                 // Reset the position and order of all objects in the list
  290.                 ResetPosition(list);
  291.  
  292.                 // Constrain everything to be within the panel's bounds
  293.                 if (keepWithinPanel) ConstrainWithinPanel();
  294.  
  295.                 // Notify the listener
  296.                 if (onReposition != null)
  297.                         onReposition();
  298.         }
  299.  
  300.         /// <summary>
  301.         /// Constrain the grid's content to be within the panel's bounds.
  302.         /// </summary>
  303.  
  304.         public void ConstrainWithinPanel ()
  305.         {
  306.                 if (mPanel != null)
  307.                         mPanel.ConstrainTargetToBounds(transform, true);
  308.         }
  309.  
  310.         /// <summary>
  311.         /// Reset the position of all child objects based on the order of items in the list.
  312.         /// </summary>
  313.  
  314.         protected void ResetPosition (BetterList<Transform> list)
  315.         {
  316.                 mReposition = false;
  317.  
  318.                 // Epic hack: Unparent all children so that we get to control the order in which they are re-added back in
  319.                 for (int i = 0, imax = list.size; i < imax; ++i)
  320.                         list[i].parent = null;
  321.  
  322.                 int x = 0;
  323.                 int y = 0;
  324.                 int maxX = 0;
  325.                 int maxY = 0;
  326.                 Transform myTrans = transform;
  327.  
  328.                 // Re-add the children in the same order we have them in and position them accordingly
  329.                 for (int i = 0, imax = list.size; i < imax; ++i)
  330.                 {
  331.                         Transform t = list[i];
  332.                         t.parent = myTrans;
  333.  
  334.                         float depth = t.localPosition.z;
  335.                         Vector3 pos = (arrangement == Arrangement.Horizontal) ?
  336.                                 new Vector3(cellWidth * x, -cellHeight * y, depth) :
  337.                                 new Vector3(cellWidth * y, -cellHeight * x, depth);
  338.  
  339.                         if (animateSmoothly && Application.isPlaying)
  340.                         {
  341.                                 SpringPosition.Begin(t.gameObject, pos, 15f).updateScrollView = true;
  342.                         }
  343.                         else t.localPosition = pos;
  344.  
  345.                         maxX = Mathf.Max(maxX, x);
  346.                         maxY = Mathf.Max(maxY, y);
  347.  
  348.                         if (++x >= maxPerLine && maxPerLine > 0)
  349.                         {
  350.                                 x = 0;
  351.                                 ++y;
  352.                         }
  353.                 }
  354.  
  355.                 // Apply the origin offset
  356.                 if (pivot != UIWidget.Pivot.TopLeft)
  357.                 {
  358.                         Vector2 po = NGUIMath.GetPivotOffset(pivot);
  359.  
  360.                         float fx, fy;
  361.  
  362.                         if (arrangement == Arrangement.Horizontal)
  363.                         {
  364.                                 fx = Mathf.Lerp(0f, maxX * cellWidth, po.x);
  365.                                 fy = Mathf.Lerp(-maxY * cellHeight, 0f, po.y);
  366.                         }
  367.                         else
  368.                         {
  369.                                 fx = Mathf.Lerp(0f, maxY * cellWidth, po.x);
  370.                                 fy = Mathf.Lerp(-maxX * cellHeight, 0f, po.y);
  371.                         }
  372.  
  373.                         for (int i = 0; i < myTrans.childCount; ++i)
  374.                         {
  375.                                 Transform t = myTrans.GetChild(i);
  376.                                 SpringPosition sp = t.GetComponent<SpringPosition>();
  377.  
  378.                                 if (sp != null)
  379.                                 {
  380.                                         sp.target.x -= fx;
  381.                                         sp.target.y -= fy;
  382.                                 }
  383.                                 else
  384.                                 {
  385.                                         Vector3 pos = t.localPosition;
  386.                                         pos.x -= fx;
  387.                                         pos.y -= fy;
  388.                                         t.localPosition = pos;
  389.                                 }
  390.                         }
  391.                 }
  392.         }
  393. }

wallabie

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 200
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #2 on: April 13, 2014, 04:11:26 AM »
Thanks I'll try this code out.

"In the future, consider asking for features, not bad-mouthing existing things. I'm always happy to adding features, but when you start complaining about X or Y, I get defensive and irritable."

I'm sorry for mouthing off.  It's not a good way of communicating.  It's just that I was told that NGUI was like Daikon but even better performance.  So I thought that it had all the features that Daikon had and made for mobile.  Initial test proved that NGUI was indeed superior in performance but once I got into the package, there are many things missing that Daikon had.  This came as an unexpected surprise and the level of frustration went up because to do similar things in NGUI took much longer than in Daikon. 

Regarding your openness to adding features,  when you see the point to something, then you do add the feature very quickly.  From my experience, there are some things you don't see right at that time so you think that we are being lazy and not wanting to build it ourselves.  In cases like this,  the message you are delivering is saying that NGUI knows best and there should not be any other way, when in fact another way is just around the corner.  Unity has many GUI solutions now besides NGUI and amongst the competition, Daikon is far the most popular.  There will be many people from the Daikon camp that need mobile will be coming to NGUI so it's definitely worth it to keep the features on an even keel between these two packages. 







ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #3 on: April 13, 2014, 06:32:49 AM »
No worries. I've cleaned up this thread to make it more on the topic. :)

P.S. The code I posted also added the delegate you can set on the grid for sorting. No need to derive from the class anymore.

wallabie

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 200
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #4 on: April 15, 2014, 11:10:51 AM »
Cool thanks for posting code with the delegate. 

As for the editing to clean up the thread... Well, It's now got a very different perspective, seems like I'm the bad guy.  How about this, I'll leave the edit as is however, I do have a request and that is for NGUI to strive to match features sets of Daikon.  It's a request that if you keep your side of the deal then it's very good business for NGUI because NGUI has everything that Daikon has and even better performance.  It's a win-win, for NGUI users and NGUI maker.

So the list:

1. Nested ScrollViews
2. Integrated Pooling system
3. Separate ScrollList component (not ScrollView adhoc), like Daikon's CoverFlow but better. Also supports pooling like 2DTK's massive scrollList.
4. Data provider component . In a similar fashion as flex, but much more lightweight and more in line with NGUI.
5. Other features of Daikon that NGUI is lacking which I haven't pointed out here.  Perhaps others can join in with their requests.


ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #5 on: April 16, 2014, 09:56:20 AM »
You can edit your own posts if you like.

Nested scroll views were functional since 3.5.6, so not sure what you're asking about here. Pooling can be done using the UIWrapContent, same component that's used to create infinite scroll views. Also keep in mind that I add features as I find good use for them. I will never add a feature just because XYZ has it. Give me a solid use case why something is needed, and I will add it. You've talked about your flex stuff on several occasions but never provided a solid example what it is, or why it is so needed. Others that chipped in didn't see it as a must have either.

wallabie

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 200
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #6 on: April 16, 2014, 12:17:00 PM »
I think to edit my own post is one thing but to be able to edit your posting is another.  In this case,  not only yours was deleted, but you also deleted my postings.  Anyhow, the way the thread looks now is considerably different.  Frankly, I did not see a reason for the editing.  You had one view, I had another.  Clearly both sides were valid and the points brought up needed to be discussed openly. 

1. As for nested scrollViews, I didn't realize that it was supported since 3.5.6, I didn't check this part of my code yet. Great news.

2. Regarding pooling, "Pooling can be done using the UIWrapContent, same component that's used to create infinite scroll views"
http://www.tasharen.com/forum/index.php?topic=9040.0
"built-in pooling feature? It just disables children if they're out of view and repositions them as necessary. It doesn't pool anything."

 What I meant by pooling is an object pooling for the component.  Perhaps this pooling system could also work in conjunction with the enabling/disabling of the Go's.  Daikon has a pooling system, although not to the extent of PoolManager but more than enabling/disabling.

3. "I will never add a feature just because XYZ has it. Give me a solid use case why something is needed, and I will add it."

I agree.  Will try to give solid use cases.

4."You've talked about your flex stuff
on several occasions but never provided a solid example what it is, or why it is so needed. Others that chipped in didn't see it as a must have either."

At first on that that thread, the one who chimed in did not think Flex's data binding was performant and did not use it. I also agreed.   However, if you look further on that thread, they had to implement their own data-binding/data provider solution for their project.  It was a custom light weight system made for NGUI. 

So  why is a data provider necessary. Usually a data provider goes together with a renderer that can be customized to use the incoming data.  Think of it as a NGUI's data binding but instead of binding properties from one component to another, it's data binding between a json/array data object to the component. 

Use cases for DataProvider with custom Renderer components:
1.  Components can be fed a json/array data and knows how  render the data by through the help of a custom data renderer created by the user.
2.  By changing the renderer,  the same incoming data can be displayed in many forms.  So instead of having to create a new component, just change the renderer.

NGUI actually has various pieces already in place and is actually using this design pattern.   

Perhaps I can explain by using an imaginary situation where we already have such a system already in place in NGUI.

For example: the ScrollView can use Grid or Table to render children items.   If we had a dataProvider component, this DP component could be attached to the ScrollView which then enables this scrollview to take in data in the form of a json/array.  Each field of the incoming data corresponds to a row of data ("name","texture2d","description",etc.)   

The user creates their own custom itemRenderer, which is made up of various NGUI components, (UIlabel, UITexture, UIbutton, etc.)  This could be saved as a prefab.  In the inspector, the dataProvider component has a property for the incoming data (json) and the itemRenderer(prefab).  Once the user specifies these two,  then the inspector exposes other fields that lets the user match up the incoming data fields to the corresponding NGUI element within the ItemRenderer. 

With this kind of data provider/ item renderer approach, it would be much easier to create data driven components very quickly.  This is the basis for how data driven components work.  In Flex, current generation Flash components, Microsoft GUi system, etc....




« Last Edit: April 16, 2014, 12:22:46 PM by wallabie »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #7 on: April 17, 2014, 01:29:12 PM »
For this to work it implies that the UI designer has to have intimate knowledge of the data that's coming in, and specify this in inspector. That screams "wrong!" to me. UI designer should create the UI layout. It's up to the developer to update the UI to display the data. Developer is the one that knows the data, not the UI designer. Developer is also the one that writes code, and it's far more performant to write a tiny script that does things like sets "label.text = dataNode.value.ToString();" inside:
  1. public UILabel itemName;
  2. public UILabel itemInfo;
  3. public UISprite itemIcon;
  4.  
  5. void UpdateUI (DataNode data)
  6. {
  7.     itemName.text = data.GetChild<string>("name");
  8.     itemInfo.text = data.GetChild<string>("info");
  9.     itemIcon.spriteName = data.GetChild<string>("icon");
  10. }
So all the designer has to do is create a UI layout and link the "itemName", "itemInfo" and "itemIcon" in inspector. The developer will do the rest. If the UI changes, the data does not need to. References to labels and sprites can be updated by the designer.

The value of this approach becomes even more evident when you take localization into consideration. What if the data needs to be localized at run-time, or pieces together using string.Format?
  1. public UILabel itemName;
  2. public UILabel itemInfo;
  3. public UISprite itemIcon;
  4. public UILabel price;
  5.  
  6. void UpdateUI (DataNode data)
  7. {
  8.     itemName.text = Localization.Get(data.GetChild<string>("name"));
  9.     itemInfo.text = Localization.Get(data.GetChild<string>("info"));
  10.     itemIcon.spriteName = data.GetChild<string>("icon");
  11.     price.text = string.Format("${0}", data.GetChild<int>("price"));
  12. }

wallabie

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 200
    • View Profile
Re: ScrollList with Grid InsertAtIndex(), getItemAtIndex()
« Reply #8 on: April 18, 2014, 04:10:34 AM »
Very good points you brought up about the responsibility between the Designer and the Programmer. 
I'm in full agreement.  Makes perfect sense.