Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,35 @@ applyTo: "src/Controls/src/Core/Handlers/Items/**,src/Controls/src/Core/Handlers
There are **TWO separate handler implementations**, but they apply to **different platforms**:

1. **Items/** (`Handlers/Items/`) - Contains code for **ALL platforms** (Android, iOS, Windows, MacCatalyst, Tizen)
2. **Items2/** (`Handlers/Items2/`) - Contains code for **iOS/MacCatalyst ONLY**
2. **Items2/** (`Handlers/Items2/`) - Primarily the **iOS/MacCatalyst** path, **plus** the **Android CarouselView Material3** handler (see exception below)

### Platform-Specific Deprecation

The deprecation of Items/ **only applies to iOS/MacCatalyst**:

| Platform | Active Handler | Notes |
|----------|----------------|-------|
| **Android** | `Items/Android/` | **ONLY implementation** - Items2/ has no Android code |
| **Windows** | `Items/` | **ONLY implementation** - Items2/ has no Windows code |
| **iOS** | `Items2/iOS/` | Items/ iOS code is deprecated |
| **MacCatalyst** | `Items2/iOS/` | Items/ MacCatalyst code is deprecated |
| Platform | Control | Active Handler | Notes |
| --------------- | ----------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- |
| **Android** | CollectionView | `Items/Android/` | **ONLY implementation** |
| **Android** | CarouselView (Material3) | `Items2/` (`CarouselViewHandler2`) | Material3-only; registered when `RuntimeFeature.IsMaterial3Enabled` is true |
| **Android** | CarouselView (non-Material3) | `Items/Android/` (`CarouselViewHandler`) | Default when Material3 is disabled |
| **Windows** | CollectionView / CarouselView | `Items/` | **ONLY implementation** - Items2/ has no Windows code |
| **iOS** | CollectionView / CarouselView | `Items2/iOS/` | Items/ iOS code is deprecated |
| **MacCatalyst** | CollectionView / CarouselView | `Items2/iOS/` | Items/ MacCatalyst code is deprecated |

**CRITICAL**: Items2/ is **iOS/MacCatalyst only**. There is NO Items2/ code for Android or Windows.
**CRITICAL**: Items2/ is **iOS/MacCatalyst only**, with **one Android exception**: the Android **CarouselView Material3** handler (`CarouselViewHandler2` + `CarouselViewHandler2.Android.cs`, `MauiCarouselRecyclerView2`, `CarouselViewAdapter2`). There is **no** Items2/ code for Android CollectionView, and **no** Items2/ code for Windows.

### Android CarouselView Material3 Exception

When `RuntimeFeature.IsMaterial3Enabled` is `true`, Android `CarouselView` is registered to
`Items2.CarouselViewHandler2` (see `AppHostBuilderExtensions.AddControlsHandlers`). This handler
lives in `Items2/` but has **Android-specific** code (`*.Android.cs` partials) that uses Material's
`CarouselLayoutManager`. Its Android types subclass the shared `Items.*` base classes
(`CarouselViewAdapter2 : Items.CarouselViewAdapter`, `MauiCarouselRecyclerView2 : Items.MauiCarouselRecyclerView`),
so changes to the shared Android carousel base classes in `Items/Android/` can affect both handlers.

- **Android CarouselView Material3 work** → `Items2/` (`CarouselViewHandler2.Android.cs` and friends)
- **Android CarouselView non-Material3 work** → `Items/Android/` (`CarouselViewHandler`)
- **Android CollectionView work** → `Items/Android/` (unchanged — no Items2 Android CollectionView)

---

Expand All @@ -32,8 +47,12 @@ The deprecation of Items/ **only applies to iOS/MacCatalyst**:
### Decision Tree by Platform

```
Is the issue/PR for Android CarouselView with Material3 enabled?
YES → Work on Items2/ (CarouselViewHandler2 Android partials)
NO → Continue...

Is the issue/PR for Android or Windows?
YES → Work on Items/ (it's the ONLY implementation)
YES → Work on Items/ (it's the ONLY implementation for these)
NO → Continue...

Is the issue/PR for iOS or MacCatalyst?
Expand All @@ -50,23 +69,27 @@ Check which handler directory the files are in:
git diff <base-branch>..<pr-branch> --name-only | grep -i "handlers/items"

# Look for path pattern:
# - Contains "/Items/Android/" → Android (ONLY implementation, work here)
# - Contains "/Items/Android/" → Android CollectionView, or non-Material3 CarouselView (work here)
# - Contains "/Items2/*.Android.cs" → Android CarouselView Material3 (CarouselViewHandler2)
# - Contains "/Items/Windows/" or ".Windows.cs" → Windows (ONLY implementation, work here)
# - Contains "/Items2/iOS/" or "Items2/*.iOS.cs" → iOS/MacCatalyst (CURRENT)
# - Contains "/Items/*.iOS.cs" (not Items2) → iOS (DEPRECATED, prefer Items2/)
```

### Default Behavior by Platform

| Platform | Default Action |
|----------|----------------|
| **Android** | ✅ Work on `Items/Android/` - it's the only option |
| **Windows** | ✅ Work on `Items/` Windows files - it's the only option |
| **iOS/MacCatalyst** | ✅ Work on `Items2/` - Items/ is deprecated for iOS |
| Platform | Default Action |
| ----------------------------------------- | -------------------------------------------------------------- |
| **Android** (CollectionView) | ✅ Work on `Items/Android/` - it's the only option |
| **Android** (CarouselView, Material3) | ✅ Work on `Items2/` - `CarouselViewHandler2` Android partials |
| **Android** (CarouselView, non-Material3) | ✅ Work on `Items/Android/` - `CarouselViewHandler` |
| **Windows** | ✅ Work on `Items/` Windows files - it's the only option |
| **iOS/MacCatalyst** | ✅ Work on `Items2/` - Items/ is deprecated for iOS |

### When to Work on Items/ for iOS (Deprecated)

Only work on Items/ iOS code when:

- PR explicitly modifies Items/ iOS files
- User explicitly requests changes to deprecated handlers
- Maintaining backward compatibility for a specific fix
Expand All @@ -75,21 +98,30 @@ Only work on Items/ iOS code when:

## Quick Reference

| Path Pattern | Platform | Status |
|--------------|----------|--------|
| `Handlers/Items/Android/` | Android | **ACTIVE** (only implementation) |
| `Handlers/Items/*.Windows.cs` | Windows | **ACTIVE** (only implementation) |
| `Handlers/Items2/iOS/` | iOS/MacCatalyst | **ACTIVE** (current) |
| `Handlers/Items/*.iOS.cs` | iOS/MacCatalyst | **DEPRECATED** (use Items2/) |
| Path Pattern | Platform | Status |
| --------------------------------------------- | ----------------------------------------------------- | ----------------------------------- |
| `Handlers/Items/Android/` | Android (CollectionView + non-Material3 CarouselView) | **ACTIVE** |
| `Handlers/Items2/*.Android.cs` (CarouselView) | Android (CarouselView, Material3) | **ACTIVE** (`CarouselViewHandler2`) |
| `Handlers/Items/*.Windows.cs` | Windows | **ACTIVE** (only implementation) |
| `Handlers/Items2/iOS/` | iOS/MacCatalyst | **ACTIVE** (current) |
| `Handlers/Items/*.iOS.cs` | iOS/MacCatalyst | **DEPRECATED** (use Items2/) |

---

## Common Mistakes to Avoid

❌ **Wrong**: "Items/ is deprecated, so I should check if Items2/ needs the same Android fix"
- Items2/ has NO Android code - there's nothing to check
❌ **Wrong**: "Items2/ has NO Android code at all"

- The Android **CarouselView Material3** handler (`CarouselViewHandler2`) lives in `Items2/` with `*.Android.cs` partials. Android **CollectionView** still has no Items2/ code.

❌ **Wrong**: "This Android CollectionView fix should also go in Items2/"

- Android CollectionView only exists in `Items/Android/`. Only Android **CarouselView Material3** is in Items2/.

❌ **Wrong**: "This Android CarouselView fix goes in Items/Android/" (without checking Material3)

- If the issue is Material3-specific, the active Android CarouselView handler is `Items2.CarouselViewHandler2`. Only the non-Material3 path lives in `Items/Android/`.

❌ **Wrong**: "This Android fix should also go in Items2/"
- Items2/ is iOS-only, Android code only exists in Items/
✅ **Correct**: "This is an Android CollectionView issue, so I work in `Items/Android/` — the only Android CollectionView implementation"

✅ **Correct**: "This is an Android-only issue, so I work in Items/Android/ which is the only Android implementation"
✅ **Correct**: "This is an Android CarouselView Material3 issue, so I work in `Items2/` (`CarouselViewHandler2.Android.cs`)"
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ public override bool OnInterceptTouchEvent(MotionEvent ev)

protected virtual bool IsHorizontal => (Carousel?.ItemsLayout)?.Orientation == ItemsLayoutOrientation.Horizontal;

// Subclasses backed by a layout manager that has no virtual-range concept
// (e.g. Material's CarouselLayoutManager) can override this to force the
// non-loop code paths regardless of the public Carousel.Loop value.
protected virtual bool IsLoopEnabled => Carousel?.Loop == true;
Comment thread
Dhivya-SF4094 marked this conversation as resolved.

protected override int DetermineTargetPosition(ScrollToRequestEventArgs args)
{
if (args.Mode == ScrollToMode.Element)
return ItemsViewAdapter.GetPositionForItem(args.Item);

if (!Carousel.Loop)
if (!IsLoopEnabled)
return args.Index;

if (_carouselViewLoopManager == null)
Expand All @@ -65,7 +70,7 @@ protected override int DetermineTargetPosition(ScrollToRequestEventArgs args)

public override bool OnTouchEvent(MotionEvent e)
{
if (Carousel.Loop)
if (IsLoopEnabled)
_carouselViewLoopManager.CenterIfNeeded(this, IsHorizontal);

return base.OnTouchEvent(e);
Expand Down Expand Up @@ -195,7 +200,7 @@ public override void ScrollTo(ScrollToRequestEventArgs args)
if (position == -1)
{
_gotoPosition = -1;
if (Carousel.Loop)
if (IsLoopEnabled)
_carouselViewLoopManager.AddPendingScrollTo(args);

return;
Expand Down Expand Up @@ -260,7 +265,7 @@ void CollectionItemsSourceChanged(object sender, System.Collections.Specialized.
else if (removingLastElement)
carouselPosition = Carousel.Position - 1;

if (Carousel.Loop)
if (IsLoopEnabled)
{
UpdateAdapter();
ScrollToPosition(carouselPosition);
Expand Down Expand Up @@ -420,7 +425,7 @@ void UpdateInitialPosition()
else
{
position = Carousel.Position;
if (Carousel.Loop && position == 0)
if (IsLoopEnabled && position == 0)
{
itemCount = ItemsViewAdapter.ItemsSource.Count;
}
Expand All @@ -430,7 +435,7 @@ void UpdateInitialPosition()

SetCurrentItem(_oldPosition);

var index = Carousel.Loop ? LoopedPosition(itemCount) + _oldPosition : _oldPosition;
var index = IsLoopEnabled ? LoopedPosition(itemCount) + _oldPosition : _oldPosition;
ScrollHelper.JumpScrollToPosition(index, Microsoft.Maui.Controls.ScrollToPosition.Center);
_gotoPosition = -1;
}
Expand Down Expand Up @@ -543,7 +548,7 @@ void CarouselViewScrolled(object sender, ItemsViewScrolledEventArgs e)

_noNeedForScroll = false;
var index = e.CenterItemIndex;
if (Carousel?.Loop == true)
if (IsLoopEnabled)
{
index = GetCarouselViewCurrentIndex(index);
}
Expand Down Expand Up @@ -607,7 +612,12 @@ void IMauiCarouselRecyclerView.UpdateFromCurrentItem()

void IMauiCarouselRecyclerView.UpdateFromPosition()
{
if (!_initialized)
// Only queue the initial-position pending scroll when looping is enabled: the queue
// is exclusively drained from loop-gated paths (LayoutReady and the scroll listener),
// so enqueuing it in the non-loop case (e.g. MauiCarouselRecyclerView2, or Loop=false)
// would leave a stale entry that is never flushed. The non-loop initial position is
// applied via the ScrollToItemPosition path below (and UpdateInitialPosition).
if (!_initialized && IsLoopEnabled)
{
_carouselViewLoopManager.AddPendingScrollTo(new ScrollToRequestEventArgs(Carousel.Position, -1, Microsoft.Maui.Controls.ScrollToPosition.Center, false));
}
Expand Down Expand Up @@ -674,7 +684,7 @@ void LayoutReady()
if (!_initialized)
{
ItemsView.Scrolled += CarouselViewScrolled;
if (Carousel.Loop)
if (IsLoopEnabled)
{
_carouselViewLoopManager.CenterIfNeeded(this, IsHorizontal);
_carouselViewLoopManager.CheckPendingScrollToEvents(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ public virtual void UpdateLayoutManager()

protected virtual RecyclerViewScrollListener<TItemsView, TItemsViewSource> CreateScrollListener() => new(ItemsView, ItemsViewAdapter);


protected virtual void UpdateSnapBehavior()
{
_snapManager = GetSnapManager();
Expand Down
Loading
Loading