Skip to content

Commit

Permalink
Merge pull request #37 from LibraStack/develop
Browse files Browse the repository at this point in the history
New functionality and bug fixes.
  • Loading branch information
ChebanovDD authored Jun 30, 2023
2 parents d50d1f2 + a875635 commit 0145369
Show file tree
Hide file tree
Showing 15 changed files with 556 additions and 40 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ The included UI elements are:
- [BindableLabel](#bindablelabel)
- [BindableTextField](#bindabletextfield)
- [BindableButton](#bindablebutton)
- [BindableDropdownField](#bindabledropdownfield)
- [BindableListView](#bindablelistview)
- [BindableScrollView](#bindablescrollview)
- [BindingContextProvider](#bindingcontextprovider)
Expand Down Expand Up @@ -840,6 +841,37 @@ The `BindableButton` can be bound to the following commands:

To pass a parameter to the viewmodel, see the [ParameterValueConverter](#parametervalueconverterttargettype) section.

#### BindableDropdownField

The `BindableDropdownField` allows the user to pick a choice from a list of options. The `BindingSelectedItemPath` attribute is optional.

```csharp
public class DropdownFieldViewModel : IBindingContext
{
public DropdownFieldViewModel()
{
var items = new ObservableCollection<string>
{
"Value 1",
"Value 2",
"Value 3"
};

Items = new ReadOnlyProperty<ObservableCollection<string>>(items);
SelectedItem = new Property<string>("Value 1");
}

public IReadOnlyProperty<ObservableCollection<string>> Items { get; }
public IProperty<string> SelectedItem { get; }
}
```

```xml
<ui:UXML xmlns:uitk="UnityMvvmToolkit.UITK.BindableUIElements" ...>
<uitk:BindableDropdownField binding-items-source-path="Items" binding-selected-item-path="SelectedItem" />
</ui:UXML>
```

#### BindableListView

The `BindableListView` control is the most efficient way to create lists. It uses virtualization and creates VisualElements only for visible items. Use the `binding-items-source-path` of the `BindableListView` to bind to an `ObservableCollection`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using UnityMvvmToolkit.Core.Interfaces;

namespace UnityMvvmToolkit.Common
{
public sealed class BindableElementData : IDisposable
{
private readonly List<IBindableElement> _bindableElements;

public BindableElementData(List<IBindableElement> bindableElements)
{
_bindableElements = bindableElements;
}

public IBindingContext BindingContext { get; set; }
public IReadOnlyList<IBindableElement> BindableElements => _bindableElements;

public void Dispose()
{
_bindableElements.Clear();
BindingContext = default;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#if UNITYMVVMTOOLKIT_TEXTMESHPRO_SUPPORT

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.CompilerServices;
using TMPro;
using UnityEngine;
using UnityMvvmToolkit.Core;
using UnityMvvmToolkit.Core.Extensions;
using UnityMvvmToolkit.Core.Interfaces;

namespace UnityMvvmToolkit.UGUI.BindableUGUIElements
{
[RequireComponent(typeof(TMP_Dropdown))]
public class BindableDropdown : MonoBehaviour, IBindableElement
{
[SerializeField] private TMP_Dropdown _dropdown;
[SerializeField] private string _bindingSelectedItemPath;
[SerializeField] private string _bindingItemsSourcePath;

private IProperty<string> _selectedItemProperty;
private IReadOnlyProperty<ObservableCollection<string>> _itemsSource;

private PropertyBindingData _selectedItemBindingData;
private PropertyBindingData _itemsSourceBindingData;

public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider)
{
if (string.IsNullOrWhiteSpace(_bindingItemsSourcePath) == false)
{
_itemsSourceBindingData ??= _bindingItemsSourcePath.ToPropertyBindingData();
_itemsSource = objectProvider
.RentReadOnlyProperty<ObservableCollection<string>>(context, _itemsSourceBindingData);
_itemsSource.Value.CollectionChanged += OnItemsCollectionChanged;
_dropdown.options = new List<TMP_Dropdown.OptionData>(_itemsSource.Value.Select(value => new TMP_Dropdown.OptionData(value)));
}

if (string.IsNullOrWhiteSpace(_bindingSelectedItemPath) == false)
{
_selectedItemBindingData ??= _bindingSelectedItemPath.ToPropertyBindingData();
_selectedItemProperty = objectProvider.RentProperty<string>(context, _selectedItemBindingData);
_selectedItemProperty.ValueChanged += OnPropertySelectedItemChanged;

var foundIndex = _dropdown.options.FindIndex(option => option.text == _selectedItemProperty.Value);
if (foundIndex != -1)
{
UpdateControlValue(foundIndex);
}

_dropdown.onValueChanged.AddListener(OnControlValueChanged);
_selectedItemProperty.Value = _dropdown.options.Count > 0 ? _dropdown.options[0].text : default;
}
}

private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (string newItem in e.NewItems)
{
_dropdown.options.Add(new TMP_Dropdown.OptionData(newItem));
}
}

if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (string oldItem in e.OldItems)
{
_dropdown.options.Remove(new TMP_Dropdown.OptionData(oldItem));
}
}

if (e.Action == NotifyCollectionChangedAction.Reset)
{
_dropdown.options.Clear();
}
}

public virtual void ResetBindingContext(IObjectProvider objectProvider)
{
if (_itemsSource != null)
{
_itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged;
objectProvider.ReturnReadOnlyProperty(_itemsSource);
_itemsSource = null;
_dropdown.options = new List<TMP_Dropdown.OptionData>();
}

if (_selectedItemProperty != null)
{
_selectedItemProperty.ValueChanged -= OnPropertySelectedItemChanged;
objectProvider.ReturnProperty(_selectedItemProperty);
_selectedItemProperty = null;
_dropdown.onValueChanged.RemoveListener(OnControlValueChanged);
}

UpdateControlValue(default);
}

protected virtual void OnControlValueChanged(int index)
{
_selectedItemProperty.Value = _dropdown.options[index].text;
}

private void OnPropertySelectedItemChanged(object sender, string newValue)
{
var foundIndex = _dropdown.options.FindIndex(option => option.text == newValue);
if (foundIndex == -1)
{
return;
}

UpdateControlValue(foundIndex);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void UpdateControlValue(int newValue)
{
_dropdown.SetValueWithoutNotify(newValue);
}
}
}

#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o

public virtual void ResetBindingContext(IObjectProvider objectProvider)
{
if (_command == null)
if (_command is null)
{
return;
}
Expand Down
Loading

0 comments on commit 0145369

Please sign in to comment.