Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

New UI Widgets

Discussion in 'Assets and Asset Store' started by ilih, Feb 11, 2015.

  1. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    I'll share the stack again when this happens as it happens randomly,
    reason im convinced this is not the datasource currently set is because when i comment the line old data source still remain there (Main Menu)

    but let me have proper stack when this happens for you to investigate,
    sorry for all this as i'm the maybe only user reusing same listview across multiple data sources , one idea however im thinking now is to keep 1 data source and then reset items from it.
     
  2. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    1. You can check Editor.log and Editor-prev.log for the stack trace, it should be there if the error happened in current or previous Unity Editor run.
    2. Is TurnBasedMatches a static variable?
    Since 2019.3 code is not always reloaded on entering play mode so static variables are not reset to default values, and this can cause bugs in editor mode.
    Way to reproduce:
    • enter play mode
    • select item for matches
    • wait until TurnBasedMatches assigned to ListView (here added callback to update ListView)
    • exit play mode
    • enter play mode again without any code changes
    • select item for matches
    • error happened (TurnBasedMatches still have the callback to update ListView from previous play mode and this cause error)
    You should add similar code to reset static variables to solve the problem:
    Code (CSharp):
    1.         [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    2.         protected static void StaticInit()
    3.         {
    4.             TurnBasedMatches = new ObservableList<WSMultiItem>();
    5.         }
     
    Last edited: May 11, 2020
  3. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi ilih,
    Looks like i found one culprit after reading your advise. i'm using Unity's experimental playmode. I just disabled it and hopefuly this error goes away

    Thanks man, i'd never had personally thought about it.
     
  4. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi @ilih

    I'm using EasyLayout to show a 2 fixed rows layout where item appear in following order (top to bottom)

    [First Item][Third Item][Fifth Item]
    [Second ITem][Fourth Item]

    which works with this setting however there is unncessary gap between item unlike a proper flow layout.
    Capture.PNG

    Please advise
     
  5. vutruc80

    vutruc80

    Joined:
    Jun 28, 2013
    Posts:
    55

    Hi @ilih,
    For my Custom Tile View, i intercept the onPointerDown event to launch my custom action.
    Code (CSharp):
    1. ForEachComponent(slot => { slot.onPointerDown.RemoveListener(OnSubmit); slot.onPointerDown.AddListener(OnSubmit);});
    As you can see in the video, the behavior is:
    - Left click on an unselected item, it will become selected
    - Left click on a selected item then my custom action will be launched
    - This behavior is very fine but if i right click on an unselected item (later part of the video), it seems that the selected item will not be changed. Then if i left click on the old selected item, the custom action will not be launched until the second left click. It seems that the first left click only reselects the item. Which is odd.
    Can you explain to me this behavior and how to fix it?
    Thank you in advance.
     
  6. Petskus

    Petskus

    Joined:
    Jan 29, 2019
    Posts:
    23
    How is get current pull distance from ScrollRectEvents ?
     
  7. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Please try Main Axis = Horizontal and LayoutType = Staggered.
    With Staggered layout, the main axis works the opposite way and it is not very intuitive.
     
  8. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Are you have some internal checks in OnSubmit function?
    Because onPointerDown event is called with every pointer down for all clicks: left, right, middle, etc.

    I think better is use onClickItem event, this will reduce checks to minimal.
    Code (CSharp):
    1.             ListView.ForEachComponent(slot =>
    2.             {
    3.                 slot.onClickItem.RemoveListener(OnSubmit);
    4.                 slot.onClickItem.AddListener(OnSubmit);
    5.             });
    6.         }
    7.  
    8.         protected int SelectedIndex = -1;
    9.  
    10.         private void OnSubmit(ListViewItem item)
    11.         {
    12.             if (ListView.IsSelected(item.Index))
    13.             {
    14.                 // if indices match then it's click on selected item
    15.                 if (SelectedIndex == item.Index)
    16.                 {
    17.                     Debug.Log("run custom action");
    18.                 }
    19.                 // otherwise save index
    20.                 else
    21.                 {
    22.                     SelectedIndex = item.Index;
    23.                 }
    24.             }
    25.         }

    ForEachComponent function is not very good to add listeners, because it works only with current instances.
    If ListView resized and more items became visible so newly created instances will not have listeners.

    Overriding AddCallback and RemoveCallback should work without a problem.
    Code (CSharp):
    1.         protected override void AddCallback(ListViewItem item)
    2.         {
    3.             base.AddCallback(item);
    4.  
    5.             item.onClickItem.AddListener(OnSubmit);
    6.         }
    7.  
    8.         protected override void RemoveCallback(ListViewItem item)
    9.         {
    10.             base.RemoveCallback(item);
    11.  
    12.             item.onClickItem.RemoveListener(OnSubmit);
    13.         }
     
    Last edited: May 14, 2020
  9. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    It can be calculated manually using ScrollRect.content.anchoredPosition.

    Or you can add following code to ScrollRectEvents.cs to use PullDistance property.
    Code (CSharp):
    1.         public float PullDistance
    2.         {
    3.             get;
    4.             protected set;
    5.         }
    And replace
    if
    's in OnDrag method:
    Code (CSharp):
    1.             if (ScrollRect.content.anchoredPosition.y < 0f)
    2.             {
    3.                 PullDistance = -ScrollRect.content.anchoredPosition.y;
    4.                 valid_distance = PullDistance >= Thresholds.Up;
    5.                 pullDirection = PullDirection.Up;
    6.             }
    7.             else if (ScrollRect.content.anchoredPosition.y > max_y)
    8.             {
    9.                 PullDistance = ScrollRect.content.anchoredPosition.y - max_y;
    10.                 valid_distance = PullDistance >= Thresholds.Down;
    11.                 pullDirection = PullDirection.Down;
    12.             }
    13.             else if (ScrollRect.content.anchoredPosition.x < 0f)
    14.             {
    15.                 PullDistance = -ScrollRect.content.anchoredPosition.x;
    16.                 valid_distance = PullDistance >= Thresholds.Left;
    17.                 pullDirection = PullDirection.Left;
    18.             }
    19.             else if (ScrollRect.content.anchoredPosition.x > max_x)
    20.             {
    21.                 PullDistance = ScrollRect.content.anchoredPosition.x - max_x;
    22.                 valid_distance = PullDistance >= Thresholds.Right;
    23.                 pullDirection = PullDirection.Right;
    24.             }
    25.             else
    26.             {
    27.                 PullDistance = 0f;
    28.             }
     
  10. vutruc80

    vutruc80

    Joined:
    Jun 28, 2013
    Posts:
    55
    - Yes i check if this is a left click or right click, different actions for different types of click. The issue is i doesn't understand why it works that way. Even with onPointerDown event, the current behavior is action is only triggered when we click on already selected item, otherwise the event is consumed elsewhere by an internal handler (i don't know where) and the new item will "auto" become "selected"
    - I will retry the onClickItem event and report the behavior.
    - Thanks for the AddCallback solution!
    - What's the semantic difference in meaning between IsSelected and SelectedIndex. I'm confused with the code: if (IsSelected(item.Index)) then SelectedIndex = item.Index; ???
     
    Last edited: May 15, 2020
  11. vutruc80

    vutruc80

    Joined:
    Jun 28, 2013
    Posts:
    55
    If we use onClickItem then it works fine because it is triggered only with Left Click then again i can't invoke custom action with Right Click. Maybe i will have to find a way to combine both events.
    EDIT 1: I think i have found the bug

    If we right click outside of selected item, at the Event System level there is no UI element selected but UIWidgets still keeps the selected item.
    What's the most elegant way to fix this bug?

    EDIT 1: my current solution:
    Code (CSharp):
    1.         protected override void Update()
    2.         {
    3.             if (SelectedIndex != -1 && EventSystem.current.currentSelectedGameObject == null)
    4.                 Deselect(SelectedIndex);
    5.         }
     
    Last edited: May 15, 2020
  12. Petskus

    Petskus

    Joined:
    Jan 29, 2019
    Posts:
    23
    Maybe it will be better add events OnPulling(direction, distanceNormal) and OnPullingStop() ?
     
  13. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    ListView has a MultipleSelect option, so ListView can have more than one selected item.
    SelectedIndex: index of the last selected item
    SelectedIndices: indices of all selected items, it returns a new List<int>, so
    ListView.SelectedIndices.Contains(index)
    is not optimal solution to check if index is selected.
    IsSelected(index): check is index is selected and no memory allocation unlike
    SelectedIndices.Contains(...)
    .
    If MultipleSelect is disabled then no difference between
    SelectedIndex==index
    and
    IsSelected(index)
    .

    It's not a bug.
    - EventSystem.current.currentSelectedGameObject is currently focused gameobject, which can be only one.
    - ListView selected items are just selected items, not necessarily currently focused gameobject.
    - ListView should not deselect items on click outside ListView.
    If selected items are deselected on click outside ListView then the selection is useless, you can not have a button (or anything else) working with selected items, because when you click items no more selected.

    If you need to deselect items on click outside ListView then you can do it this way:
    Code (CSharp):
    1.         Camera CurrentCamera;
    2.  
    3.         void Update()
    4.         {
    5.             var is_click = Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2);
    6.             if (is_click)
    7.             {
    8.                 Vector2 point;
    9.                 // if mouse position is outside ListView
    10.                 if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(ListView.transform as RectTransform, Input.mousePosition, CurrentCamera, out point))
    11.                 {
    12.                     ListView.Deselect(ListView.SelectedIndex);
    13.                 }
    14.             }
    15.         }
     
  14. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Not sure, I think
    UnityEvent<ScrollRectEvents, PullDirection>
    will be better if you need to distance then probably you also need Thresholds values and maybe some other values too.
    I do not think OnPullingStop is required, already have OnPull (on success), and OnPullCancel (on cancel) events.

    Code (CSharp):
    1.  
    2.         [Serializable]
    3.         public class PullingEvent : UnityEvent<ScrollRectEvents, PullDirection>
    4.         {
    5.         }
    6.  
    7.         [SerializeField]
    8.         public PullingEvent OnPulling = new PullingEvent();
    9.  
    10.         public virtual void OnDrag(PointerEventData eventData)
    11.         {
    12.             ....
    13.  
    14.             OnPulling.Invoke(this, pullDirection);
    15.  
    16.             if (valid_distance)
    17.             ....
    18.         }
     
  15. tim44

    tim44

    Joined:
    May 15, 2019
    Posts:
    37
    I may be missing something obvious, but is there a way to reformat all items in a list? This code is working, but thought there might be an easy way without recreating the list:
    Code (CSharp):
    1. public void refreshListView(List<ListViewItemDescription> mList, GameObject listView)
    2.     {
    3.  
    4.         var obj = listView.GetComponent<ListView>();
    5.         obj.DataSource.Clear();
    6.         obj.DataSource.AddRange(mList);
    7.  
    8.     }
     
  16. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Fastest way is to use
    ListView.Resize()
    method or change
    ListViewCustom.UpdateView()
    from
    protected
    to
    public
    and use it (this method will be changed to
    public
    in next release).

    Right way is add INotifyPropertyChanged implementation to ListViewItemDescription class.
    Public fields should be converted to property and raise
    PropertyChanged()
    event when any property changed.
    This way ListView will be automatically updated if any property changed.

    Old code:
    Code (CSharp):
    1.     [Serializable]
    2.     public class ListViewItemDescription
    3.     {
    4.         public string Name;
    5.     }
    New code:
    Code (CSharp):
    1.     [Serializable]
    2.     public class ListViewItemDescription : INotifyPropertyChanged
    3.     {
    4.         [SerializeField] // required because field is not public anymore
    5.         [FormerlySerializedAs("Name")] // required to avoid serialized data loss with the field name changed
    6.         private string name;
    7.  
    8.         public string Name // replace field with public property with same name
    9.         {
    10.             get => name;
    11.  
    12.             set
    13.             {
    14.                 if (name != value)
    15.                 {
    16.                     name = value;
    17.                     Changed();
    18.                 }
    19.             }
    20.         }
    21.  
    22.         protected void Changed([CallerMemberName] string propertyName = "")
    23.         {
    24.             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    25.         }
    26.  
    27.         public event PropertyChangedEventHandler PropertyChanged = Utilities.DefaultPropertyHandler;
    28.     }
     
    Last edited: May 15, 2020
    tim44 likes this.
  17. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi @ilih,
    Is there an even for datasource change of ListView?
    an event which triggers whenever i call ListView.DataSource = DS;
     
  18. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    OnUpdateView(), but this event also happens on a ListView scroll and any DataSource changes.
     
    jGate99 likes this.
  19. vutruc80

    vutruc80

    Joined:
    Jun 28, 2013
    Posts:
    55


    Hi @ilih , further testing your solution, i'm observing this issue. A page normally has 3 x 4 = 12 items, as you can see in the load grid (having 24 items) everything seems fine. But in save grid (having 25 items) somehow the tileview only shows 3 blocks. Do you have any suggestion of where this issue probably comes from. Thank you very much for the great support :)
     
  20. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Are TileViews settings identical?
    If yes, then something was wrong with a calculation of how many items are visible.
    Probably ScrollRect size was changed, or DefaultItem size was changed since the initial calculation.
    Please try to use
    ListView.Resize()
    or
    ListView.ChangeDefaultItemSize(new Vector(...))
    after click on load when ListView gameobject already active.
     
  21. vutruc80

    vutruc80

    Joined:
    Jun 28, 2013
    Posts:
    55
    Yes they are identical. And no calling ListView.Resize() doesn't solve the issue. I made a wild guess that there is something to do with the execution order so i converted UpdateMargin into a Coroutine
    Code (CSharp):
    1.        void UpdateMarginNextFrame()
    2.         {
    3.             StartCoroutine(UpdateMargin());
    4.         }
    then it works now.
    But after i makes a change in DataSource (adding a new item) then the issues reappears and i notice that UpdateMargin isn't invoked so i try to add:
    Code (CSharp):
    1. ListView.DataSource.OnChange += UpdateMarginNextFrame;
    but it doesn't work.
    Any idea?

    EDIT 1: This is the ItemsPewRow calculation code in the method CalculateMaxVisibleItems() of class TileViewTypeFixed:
    Code (CSharp):
    1.                 var width = Owner.ScrollRectSize.x + spacing_x - Owner.LayoutBridge.GetFullMarginX();
    2.                 var height = Owner.ScrollRectSize.y + spacing_y - Owner.LayoutBridge.GetFullMarginY();
    3.  
    4.                 if (Owner.IsHorizontal())
    5.                 {
    6.                     ItemsPerRow = Mathf.CeilToInt(width / (Owner.ItemSize.x + spacing_x)) + 1;
    7.                     ItemsPerRow = Mathf.Max(MinVisibleItems, ItemsPerRow);
    8.  
    9.                     ItemsPerColumn = Mathf.FloorToInt(height / (Owner.ItemSize.y + spacing_y));
    10.                     ItemsPerColumn = Mathf.Max(1, ItemsPerColumn);
    11.                     ItemsPerColumn = Owner.LayoutBridge.RowsConstraint(ItemsPerColumn);
    12.                 }
    So if we increase the margin(GetFullMarginX()), the width will be decreased and ItemsPerRow will be also decreased. It seems that we must restore the original margin value before the execution of this method

    EDIT 2: I make it works now, with your old PaginatorFix code plus a dirty hack in class TileViewTypeFixed
    Code (CSharp):
    1.                 //var width = Owner.ScrollRectSize.x + spacing_x - Owner.LayoutBridge.GetFullMarginX();
    2.                 var width = Owner.ScrollRectSize.x + spacing_x;
    But it will be a painful job of maintenance if i ever want to update the UI Widgets library. Any suggestion for a more elegant solution?

    EDIT 3: We still have to wrap UpdateMargin in a CoRoutine and execute it several frames later so that every info has been correctly updated before its execution
     
    Last edited: May 18, 2020
  22. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Thanks, I understood the problem and working on a solution.
     
  23. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Fixed in v1.12.2b1
     
    vutruc80 likes this.
  24. cgrow67

    cgrow67

    Joined:
    May 31, 2016
    Posts:
    8
    Would it be possible to get double click support / events for the ListView and TileView ?? I've rigged some stuff from the outside but its almost impossible as changing selections loses a mouse click.
     
  25. cgrow67

    cgrow67

    Joined:
    May 31, 2016
    Posts:
    8
    I updated to 1.12.1 today. There seems to be an error in the asmdef as there was no reference to TMPro. It was enabled in my project before update New UI Widgets. I guess it didn't think TextMeshPro was enabled.. Not sure.. so I updated the asmdef files and now it all compiles.

    When I drop a widgets onto my scene they show up all white as though the theme or some assets are missing. I've also tried applying theme and this doesn't work either.

    I'm using Unity3d 2019.3.13f1. I've also installed New UI Widgets 1.12.2b1 for Unity 2019.3.0f6 and still the same result.

    Okay.. that was some stress.. but finally muddled my way though it .. Seems going to build settings and changing the platform to another platform finally got the missing assets setup. My application once again looks good and new widgets look correct as well. Do you think I should continue with the 1.12.2b1 ?
     
    Last edited: May 19, 2020
  26. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    You can use DefaultItem.onDoubleClick event.
    Like this:
    Code (CSharp):
    1.     public class YourListView : ListViewCustom<YourListViewItemComponent, YourListViewItem>
    2.     {
    3.         protected override void AddCallback(ListViewItem item)
    4.         {
    5.             base.AddCallback(item);
    6.             item.onDoubleClick.AddListener(ProcessDoubleClick);
    7.         }
    8.  
    9.         protected override void RemoveCallback(ListViewItem item)
    10.         {
    11.             base.RemoveCallback(item);
    12.             item.onDoubleClick.RemoveListener(ProcessDoubleClick);
    13.         }
    14.  
    15.         void ProcessDoubleClick(int index)
    16.         {
    17.             Debug.Log("double click: " + DataSource[index]);
    18.         }
    19.  
    or this:
    Code (CSharp):
    1.     public class YourListViewItemComponent : ListViewItem, IViewData<YourListViewItem>
    2.     {
    3.         protected override void Start()
    4.         {
    5.             base.Start();
    6.  
    7.             onDoubleClick.AddListener(ProcessDoubleClick);
    8.         }
    9.  
    10.         void ProcessDoubleClick(int index)
    11.         {
    12.             Debug.Log("double click: " + (Owner as YourListView).DataSource[index]);
    13.         }
    Package has code that should restore TMPro reference after the update, but either previous version was old, and without such code or the code does not work for some reason, I'll check it.
    Disable TMPro support and enabling it back solves the problem.

    It can happen because of the empty style used as default and it automatically applied to newly created widgets.
    Please open "New UI Widgets/Styles/UIWidgets Style Default," check it values (it should not be all white color or null), and set it as default.
    If UIWidgets Style Default values are all white color or null, then try to import package again, sometimes import works incorrectly.

    You can use "t:UIWidgets.Styles.Style" to find all styles and check which one is used by default.
     
    Last edited: May 19, 2020
    cgrow67 likes this.
  27. cgrow67

    cgrow67

    Joined:
    May 31, 2016
    Posts:
    8
    Thank you so much. The double click handler works perfectly!

    Yes, I can see that Import if flawed. This is something they really need to fix.

    Got the style setup and back to my custom style.

    We really appreciate the quick response!
     
    ilih likes this.
  28. Petskus

    Petskus

    Joined:
    Jan 29, 2019
    Posts:
    23
    An error was founded in the Spinner with Min=2, Max=10.
    The value will never become 10 because the Spinner.ValidateFull () checks the new value char by char.
    The first "1"is not inbound and the result is reset.
     
  29. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    You can change validation from "On Key Down" to "On End Input", in this case, validation will be done when Spinner.InputField loses focus
     
    Petskus likes this.
  30. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi @ilih
    if modal is true, then i also want to hide close icon (top right) in dialogs.
    Please advise
    Thanks
     
  31. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    You can either use a different template for the modal Dialog without a close button or use wrapper function for the Show() to show/hide the close button on condition.
    Code (CSharp):
    1.     DialogTemplate.Clone().ShowModal(....)
    2.  
    3.     public static class DialogExtensions
    4.     {
    5.         public static void ShowModal(this Dialog dialog,
    6.             string title = null,
    7.             string message = null,
    8.             IList<DialogButton> buttons = null,
    9.             string focusButton = null,
    10.             Vector3? position = null,
    11.             Sprite icon = null,
    12.             bool modal = false,
    13.             Sprite modalSprite = null,
    14.             Color? modalColor = null,
    15.             Canvas canvas = null,
    16.             RectTransform content = null,
    17.             Action onClose = null,
    18.             Func<int, bool> onCancel = null)
    19.         {
    20.             var button = dialog.transform.Find("Header/CloseButton");
    21.             if (button != null)
    22.             {
    23.                 button.gameObject.SetActive(!modal);
    24.             }
    25.  
    26.             dialog.Show(title, message, buttons, focusButton, position, icon, modal, modalSprite, modalColor, canvas, content, onClose, onCancel);
    27.         }
    28.     }
     
    jGate99 likes this.
  32. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi @ilih,
    I extend Dialog class to handle some custom cases, however there is one problem that Clone() method returns base class Dialog and not my extend class. I can cast it, but i want to avoid casting.
    Please advise
     
  33. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    I do not think it is possible to avoid type casting at all in these cases.
    You can limit the type casting to a single function like this:
    Code (CSharp):
    1.         public new DialogNew Clone()
    2.         {
    3.             return base.Clone() as DialogNew;
    4.         }
    Or you can try to use extensions methods instead of the derived class if it is possible.
     
    jGate99 likes this.
  34. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,029
    Hi @ilih
    When i pass the same array of buttons (DialogButton[] buttons)
    to a dialog, every time i show
    button orders keep changing in alert itself.
    For example if its "OK", and "Cancel"
    then one time "Cancel" appear in start, and then other time cancel appear in end and it keep happening.
     
  35. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    796
    Sorry, fixed in v1.12.2b3
     
    jGate99 likes this.
  36. Arcanor

    Arcanor

    Joined:
    Nov 4, 2009
    Posts:
    277
    @ilih - I'm still so very impressed at how much effort you put in to support this package. Thank you so much! It makes me much more confident as a user to know that problems will be fixed. You rock!
     
    jGate99, hopeful and ilih like this.
unityunity