Option Strict On
Option Explicit On

Imports System
Imports System.Reflection
Imports Nexamas.UI.Animation.Lottie
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp


Namespace Nexamas.UI.Components

    Friend NotInheritable Class TopBarMenuButtonClickEventArgs
        Inherits EventArgs

        Friend Sub New(barRectPx As SKRect, buttonRectPx As SKRect)
            Me.BarRectPx = barRectPx
            Me.ButtonRectPx = buttonRectPx
        End Sub

        Friend ReadOnly Property BarRectPx As SKRect
        Friend ReadOnly Property ButtonRectPx As SKRect
    End Class

    Friend NotInheritable Class TopBarEngine
        Implements IDisposable
        Private Shared ReadOnly _topBarContract As MASResolvedComponentContract =
    MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASTopBarComponent))

        Private ReadOnly _painter As TopBarPainter
        Private ReadOnly _controller As New TopBarController()
        Private ReadOnly _item As New TopBarItem()
        Private ReadOnly _menuButtonPainter As New TopBarMenuButtonPainter()
        Private _menuButtonMotionRunner As MASMotionTimelineRunner

        Private _host As ITopBarHostActions
        Private ReadOnly _inv As ITopBarInvalidator
        Private ReadOnly _motionFrameClockProvider As ITopBarMotionFrameClockProvider

        Private _dpi As Single = 1.0F
        Private _disposed As Boolean
        Private _dragging As Boolean

        Private _menuButtonRectPx As SKRect = SKRect.Empty
        Private _menuButtonHover As Boolean
        Private _menuButtonPressed As Boolean
        Private _menuButtonOpen As Boolean

        Friend Event MenuButtonClicked As EventHandler(Of TopBarMenuButtonClickEventArgs)

        Friend Sub New(primitives As MASVisualPrimitivesPainter,
                       host As ITopBarHostActions,
                       Optional invalidator As ITopBarInvalidator = Nothing,
                       Optional motionFrameClockProvider As ITopBarMotionFrameClockProvider = Nothing)

            If primitives Is Nothing Then Throw New ArgumentNullException(NameOf(primitives))
            If host Is Nothing Then Throw New ArgumentNullException(NameOf(host))

            _painter = New TopBarPainter(primitives)
            _host = host
            _inv = invalidator
            _motionFrameClockProvider = motionFrameClockProvider

            Try
                _menuButtonPainter.TryLoadMenuLottieFromResource(
    GetType(TopBarEngine).Assembly,
    "menubutton.json"
)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException1)
            End Try
        End Sub

        Friend Property HostActions As ITopBarHostActions
            Get
                Return _host
            End Get
            Set(value As ITopBarHostActions)
                If value Is Nothing Then Return
                _host = value
            End Set
        End Property

        Friend Property ShowLogo As Boolean
            Get
                Return _item.ShowLogo
            End Get
            Set(value As Boolean)
                If _item.ShowLogo = value Then Return
                _item.ShowLogo = value
                SafeInvalidate()
            End Set
        End Property

        Friend Sub ConfigureMenuLottie(asm As Assembly, resourceName As String)
            If _disposed Then Return

            Try
                _menuButtonPainter.TryLoadMenuLottieFromResource(asm, resourceName)
                _menuButtonPainter.InitializeMenuLottieState(_menuButtonOpen)
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException2)
            End Try

            SafeInvalidate()
        End Sub

        Friend Sub SetMenuButtonOpenVisual(isOpen As Boolean)
            If _disposed Then Return
            If _menuButtonOpen = isOpen Then Return

            Dim previousOpen As Boolean = _menuButtonOpen
            _menuButtonOpen = isOpen
            StartMenuButtonMotion(previousOpen)
            SafeInvalidate()
        End Sub

        Friend Property Title As String
            Get
                Return _item.Title
            End Get
            Set(value As String)
                _item.Title = MASTopBarChromePolicy.NormalizeTitle(value)
                SafeInvalidate()
            End Set
        End Property

        Friend Property SurfaceMaterialKey As String
            Get
                Return _item.SurfaceMaterialKey
            End Get
            Set(value As String)
                Dim key As String = MASTopBarChromePolicy.NormalizeSurfaceMaterial(value)
                If String.Equals(_item.SurfaceMaterialKey, key, StringComparison.OrdinalIgnoreCase) Then Return
                _item.SurfaceMaterialKey = key
                SafeInvalidate()
            End Set
        End Property

        Private Shared Function NormalizeDpi(dpi As Single) As Single
            Return MASTopBarChromePolicy.NormalizeDpi(dpi)
        End Function

        Friend Sub Render(canvas As SKCanvas, ctx As MASThemeContext, barRectPx As SKRect, timeSeconds As Single)
            If _disposed Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If barRectPx.IsEmpty OrElse barRectPx.Width <= TopBarTokens.MinimumUsablePx OrElse barRectPx.Height <= TopBarTokens.MinimumUsablePx Then Return

            _dpi = NormalizeDpi(ctx.Dpi)

            _item.BarRectPx = barRectPx
            _item.Enabled = True

            _menuButtonRectPx = ResolveMenuButtonRect(barRectPx, _dpi)

            Dim leftReservedPx As Single = 0.0F
            If Not _menuButtonRectPx.IsEmpty Then
                leftReservedPx = (_menuButtonRectPx.Right - barRectPx.Left) + (MenuTokens.DropDownPanelGapX * _dpi)
            End If

            _controller.UpdateLayout(
                item:=_item,
                hostRectPx:=barRectPx,
                dpi:=_dpi,
                leftReservedPx:=leftReservedPx
            )

            _painter.Render(
    canvas:=canvas,
    ctx:=ctx,
    contract:=_topBarContract,
    item:=_item,
    dpi:=_dpi,
    timeSeconds:=timeSeconds)

            _menuButtonPainter.Render(
                canvas:=canvas,
                ctx:=ctx,
                dpi:=_dpi,
                boundsPx:=_menuButtonRectPx,
                isHovered:=_menuButtonHover,
                isPressed:=_menuButtonPressed,
                isOpen:=_menuButtonOpen,
                isEnabled:=True
            )
        End Sub

        Friend Sub PointerMove(ptPx As SKPoint)
            If _disposed Then Return

            Dim changed As Boolean = False

            Dim oldHoverButton As Boolean = _menuButtonHover
            _menuButtonHover = HitTestMenuButton(ptPx)

            If oldHoverButton <> _menuButtonHover Then
                changed = True
            End If

            If _menuButtonHover Then
                If changed Then SafeInvalidate()
                Return
            End If

            If _dragging Then Return

            Dim oldHoverDrag As Boolean = _item.IsHoveringDragZone
            _controller.UpdateHover(_item, ptPx)

            If _item.IsHoveringDragZone <> oldHoverDrag Then
                changed = True
            End If

            If changed Then SafeInvalidate()
        End Sub

        Friend Function PointerDown(ptPx As SKPoint, Optional screenPt As SKPoint? = Nothing) As Boolean
            If _disposed Then Return False

            _menuButtonPressed = HitTestMenuButton(ptPx)

            If _menuButtonPressed Then
                SafeInvalidate()
                Return True
            End If

            If _item.HitTestDragZone(ptPx) Then
                ' Native HTCAPTION dragging owns the drag loop. Keeping MAS pointer capture
                ' here can strand TopBar as the active input owner when WinForms does not
                ' deliver the matching MouseUp back to the SK host.
                _dragging = False

                If screenPt.HasValue Then
                    _host.BeginDrag(screenPt.Value)
                End If
            End If

            SafeInvalidate()
            Return False
        End Function

        Friend Sub PointerUp(ptPx As SKPoint, Optional screenPt As SKPoint? = Nothing)
            If _disposed Then Return

            Dim wasMenuPress As Boolean = _menuButtonPressed
            _menuButtonPressed = False

            If wasMenuPress AndAlso HitTestMenuButton(ptPx) Then
                RaiseEvent MenuButtonClicked(
                    Me,
                    New TopBarMenuButtonClickEventArgs(
                        _item.BarRectPx,
                        _menuButtonRectPx
                    )
                )

                SafeInvalidate()
                Return
            End If

            If _dragging Then
                _dragging = False
                _host.EndDrag()
            End If

            SafeInvalidate()
        End Sub

        Friend Sub PointerLeave()
            If _disposed Then Return

            _menuButtonHover = False
            _menuButtonPressed = False

            _item.IsHoveringDragZone = False

            If _dragging Then
                _dragging = False
                _host.EndDrag()
            End If

            SafeInvalidate()
        End Sub

        Friend Sub PointerMoveScreen(screenPt As SKPoint)
            If _disposed OrElse Not _dragging Then Return
            _host.DragTo(screenPt)
        End Sub

        Friend Function WantsCaptureAt(ptPx As SKPoint) As Boolean
            If _disposed Then Return False
            Return HitTestMenuButton(ptPx)
        End Function


        Private Sub StartMenuButtonMotion(previousOpen As Boolean)
            If _disposed Then Return

            StopMenuButtonMotion()

            If Not _menuButtonPainter.IsLottieLoaded Then Return

            If Not _menuButtonPainter.IsMenuLottieStateInitialized Then
                _menuButtonPainter.InitializeMenuLottieState(previousOpen)
            End If

            Dim fromProgress As Single = _menuButtonPainter.CurrentMenuLottieProgress
            Dim targetProgress As Single = TopBarMenuButtonPainter.ResolveMenuLottieTarget(_menuButtonOpen)

            If Math.Abs(fromProgress - targetProgress) <= 0.0001F Then
                _menuButtonPainter.SetMenuLottieProgress(targetProgress)
                SafeInvalidate()
                Return
            End If

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateDistanceScaledTransitionPlan(
                MASMotionTokenId.MenuButtonMorph,
                fromProgress,
                targetProgress,
                TopBarMenuButtonPainter.MenuLottieMorphDistance)

            If Not plan.ShouldAnimate Then
                _menuButtonPainter.SetMenuLottieProgress(targetProgress)
                SafeInvalidate()
                Return
            End If

            Dim motionFrameClock As MASMotionFrameClock = ResolveMotionFrameClock()

            If motionFrameClock Is Nothing Then
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(
                    New InvalidOperationException("TopBar menu-button Lottie motion requested without a host-scoped MASMotionFrameClock."),
                    "TopBar.MenuButtonMorph.MissingHostFrameClock")
                _menuButtonPainter.SetMenuLottieProgress(targetProgress)
                SafeInvalidate()
                Return
            End If

            _menuButtonMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="TopBar.MenuButtonMorph",
                plan:=plan,
                requestFrame:=AddressOf SafeInvalidate,
                applyFrame:=AddressOf ApplyMenuButtonMotionFrame,
                completed:=AddressOf CompleteMenuButtonMotionFrame,
                motionFrameClock:=motionFrameClock)
            _menuButtonMotionRunner.Start()
        End Sub

        Private Sub ApplyMenuButtonMotionFrame(frame As MASMotionFrame)
            If _disposed OrElse frame Is Nothing Then Return
            _menuButtonPainter.SetMenuLottieProgress(frame.Value)
            SafeInvalidate()
        End Sub

        Private Sub CompleteMenuButtonMotionFrame(frame As MASMotionFrame)
            If _disposed Then Return
            _menuButtonPainter.SetMenuLottieProgress(TopBarMenuButtonPainter.ResolveMenuLottieTarget(_menuButtonOpen))
            _menuButtonMotionRunner = Nothing
            SafeInvalidate()
        End Sub

        Private Sub StopMenuButtonMotion()
            If _menuButtonMotionRunner Is Nothing Then Return
            _menuButtonMotionRunner.Dispose()
            _menuButtonMotionRunner = Nothing
        End Sub

        Private Function HitTestMenuButton(ptPx As SKPoint) As Boolean
            If _menuButtonRectPx.IsEmpty Then Return False
            Return _menuButtonRectPx.Contains(ptPx.X, ptPx.Y)
        End Function

        Private Shared Function ResolveMenuButtonRect(barRectPx As SKRect, dpi As Single) As SKRect
            dpi = NormalizeDpi(dpi)

            Dim sizePx As Single = Math.Min(TopBarTokens.MenuButtonMaxSize * dpi, Math.Max(TopBarTokens.MinimumUsablePx, barRectPx.Height - (TopBarTokens.MenuButtonVerticalInset * dpi)))
            Dim leftPx As Single = barRectPx.Left + (TopBarTokens.MenuButtonLeftInset * dpi)
            Dim topPx As Single = barRectPx.Top + ((barRectPx.Height - sizePx) * 0.5F)

            Return PixelSnap.SnapRectHalf(New SKRect(
                leftPx,
                topPx,
                leftPx + sizePx,
                topPx + sizePx
            ))
        End Function

        Private Function ResolveMotionFrameClock() As MASMotionFrameClock
            If _disposed OrElse _motionFrameClockProvider Is Nothing Then Return Nothing

            Try
                Return _motionFrameClockProvider.TryGetMotionFrameClock()
            Catch masCaughtExceptionMotionClock As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(masCaughtExceptionMotionClock, "TopBarEngine.MotionFrameClock")
                Return Nothing
            End Try
        End Function

        Private Sub SafeInvalidate()
            Try
                _inv?.Invalidate()
            Catch masCaughtException3 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException3)
            End Try
        End Sub

        Public Sub Dispose() Implements IDisposable.Dispose
            If _disposed Then Return
            StopMenuButtonMotion()
            _disposed = True

            Try
                _menuButtonPainter.Dispose()
            Catch masCaughtException4 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException4)
            End Try
            Try
                _painter.Dispose()
            Catch masCaughtException5 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException5)
            End Try
        End Sub

        Private NotInheritable Class TopBarMenuButtonPainter
            Implements IDisposable

            Private Shared ReadOnly _buttonContract As MASResolvedComponentContract =
    MASArchitectureRuntime.ResolveContractOrThrow(GetType(GlyphButton))

            Private ReadOnly _fill As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Fill
            }

            Private ReadOnly _stroke As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Stroke
            }

            Private ReadOnly _icon As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeJoin = SKStrokeJoin.Round
            }

            Private ReadOnly _lottie As New MASLottiePlayer()

            Private _lottieLoaded As Boolean
            Private _stateInitialized As Boolean
            Private _currentProgress As Single = 0.0F

            Private _disposed As Boolean

            Friend Sub TryLoadMenuLottieFromResource(asm As Assembly, resourceName As String)
                If _disposed Then Return
                If asm Is Nothing Then Return
                If String.IsNullOrWhiteSpace(resourceName) Then Return

                Try
                    _lottie.LoadFromEmbeddedResource(asm, resourceName)
                    _lottieLoaded = _lottie.IsLoaded

                    _stateInitialized = False
                    _currentProgress = 0.0F
                Catch masCaughtException11 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException11)
                    _lottieLoaded = False
                    _stateInitialized = False
                End Try
            End Sub

            Friend ReadOnly Property IsLottieLoaded As Boolean
                Get
                    Return _lottieLoaded
                End Get
            End Property

            Friend ReadOnly Property IsMenuLottieStateInitialized As Boolean
                Get
                    Return _stateInitialized
                End Get
            End Property

            Friend ReadOnly Property CurrentMenuLottieProgress As Single
                Get
                    Return _currentProgress
                End Get
            End Property

            Friend Const MenuLottieClosedProgress As Single = 0.0F
            Friend Const MenuLottieOpenProgress As Single = 0.5F
            Friend Const MenuLottieMorphDistance As Single = MenuLottieOpenProgress - MenuLottieClosedProgress

            Friend Shared Function ResolveMenuLottieTarget(isOpen As Boolean) As Single
                Return If(isOpen, MenuLottieOpenProgress, MenuLottieClosedProgress)
            End Function

            Friend Sub InitializeMenuLottieState(isOpen As Boolean)
                If _disposed OrElse Not _lottieLoaded Then Return
                If _stateInitialized Then Return

                _stateInitialized = True
                SetMenuLottieProgress(ResolveMenuLottieTarget(isOpen))
            End Sub

            Friend Sub SetMenuLottieProgress(progress As Single)
                If _disposed OrElse Not _lottieLoaded Then Return

                If Single.IsNaN(progress) OrElse Single.IsInfinity(progress) Then progress = 0.0F
                If progress < 0.0F Then progress = 0.0F
                If progress > 0.5F Then progress = 0.5F

                _currentProgress = progress
                _lottie.SeekNormalized(_currentProgress)
            End Sub

            Friend Sub Render(canvas As SKCanvas,
                              ctx As MASThemeContext,
                              dpi As Single,
                              boundsPx As SKRect,
                              isHovered As Boolean,
                              isPressed As Boolean,
                              isOpen As Boolean,
                              isEnabled As Boolean)

                If _disposed Then Return
                If canvas Is Nothing OrElse ctx Is Nothing Then Return
                If boundsPx.IsEmpty OrElse boundsPx.Width <= TopBarTokens.MinimumUsablePx OrElse boundsPx.Height <= TopBarTokens.MinimumUsablePx Then Return

                dpi = NormalizeDpi(dpi)

                Dim r As SKRect = PixelSnap.SnapRectHalf(boundsPx)
                Dim radius As Single = PixelSnap.SafeRadius(TopBarTokens.MenuButtonRadius * dpi)

                If _lottieLoaded Then
                    RenderLottie(canvas, r, dpi, isOpen)
                Else
                    DrawButtonChrome(canvas, ctx, dpi, r, radius, isHovered, isPressed, isOpen, isEnabled)
                    DrawFallbackHamburger(canvas, ctx, dpi, r, isEnabled)
                End If
            End Sub

            Private Sub RenderLottie(canvas As SKCanvas,
                                     boundsPx As SKRect,
                                     dpi As Single,
                                     isOpen As Boolean)

                InitializeMenuLottieState(isOpen)

                canvas.Save()

                Try
                    Dim rr As New SKRoundRect()
                    dpi = NormalizeDpi(dpi)
                    Dim radius As Single = PixelSnap.SafeRadius(TopBarTokens.MenuButtonRadius * dpi)

                    rr.SetRectRadii(
                        rect:=boundsPx,
                        radii:=New SKPoint() {
                            New SKPoint(radius, radius),
                            New SKPoint(radius, radius),
                            New SKPoint(radius, radius),
                            New SKPoint(radius, radius)
                        }
                    )

                    canvas.ClipRoundRect(rr)
                    _lottie.Render(canvas, boundsPx)
                Finally
                    canvas.Restore()
                End Try
            End Sub

            Private Sub DrawButtonChrome(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          dpi As Single,
                                          r As SKRect,
                                          radius As Single,
                                          isHovered As Boolean,
                                          isPressed As Boolean,
                                          isOpen As Boolean,
                                          isEnabled As Boolean)

                Dim menuTheme As IMASMenuTheme = If(ctx.Theme Is Nothing, Nothing, MASAppThemeContracts.Families(ctx.Theme).Menu)
                If menuTheme Is Nothing Then Return

                Dim itemSurface As MASMenuItemSurfaceTheme = menuTheme.ItemSurface

                If itemSurface.FillTop.Alpha > 0 OrElse itemSurface.FillBottom.Alpha > 0 Then
                    Using sh As SKShader = SKShader.CreateLinearGradient(
                        New SKPoint(r.Left, r.Top),
                        New SKPoint(r.Left, r.Bottom),
                        New SKColor() {
                            itemSurface.FillTop,
                            ColorMath.Mix(itemSurface.FillTop, itemSurface.FillBottom, 0.52F),
                            itemSurface.FillBottom
                        },
                        New Single() {0.0F, 0.52F, 1.0F},
                        SKShaderTileMode.Clamp
                    )
                        _fill.Shader = sh
                        _fill.Color = SKColors.White
                        _fill.BlendMode = SKBlendMode.SrcOver
                        canvas.DrawRoundRect(r, radius, radius, _fill)
                        _fill.Shader = Nothing
                    End Using
                End If

                If itemSurface.Border.Alpha > 0 Then
                    _stroke.Shader = Nothing
                    _stroke.Color = itemSurface.Border
                    _stroke.StrokeWidth = Math.Max(1.0F, 1.0F * dpi)

                    Dim borderRect As SKRect = PixelSnap.InsetAll(r, _stroke.StrokeWidth * 0.5F)
                    canvas.DrawRoundRect(borderRect, radius, radius, _stroke)
                End If

                Dim state As MASVisualInteractionState = MASVisualInteractionState.Normal

                If Not isEnabled Then
                    state = MASVisualInteractionState.Disabled
                ElseIf isPressed Then
                    state = MASVisualInteractionState.Pressed
                ElseIf isOpen Then
                    state = MASVisualInteractionState.Pressed
                ElseIf isHovered Then
                    state = MASVisualInteractionState.Hover
                End If

                If state <> MASVisualInteractionState.Normal Then
                    Dim masState As MASInteractionState =
    ToMASInteractionState(state)

                    If masState <> MASInteractionState.Enabled Then
                        Dim spec As MASVisualInteractionOverlaySpec =
        MASInteractionResolver.Build(
            contract:=_buttonContract,
            state:=masState,
            theme:=ctx.Theme)

                        If spec.FillEnabled OrElse spec.RingEnabled Then
                            MASSurfaceInteractionRing.DrawOverlay(
            canvas:=canvas,
            baseRectPx:=r,
            baseRadiusPx:=radius,
            spec:=spec,
            dpi:=dpi)
                        End If
                    End If
                End If
            End Sub
            Private Shared Function ToMASInteractionState(state As MASVisualInteractionState) As MASInteractionState
                Select Case state
                    Case MASVisualInteractionState.Hover
                        Return MASInteractionState.Hovered

                    Case MASVisualInteractionState.Pressed
                        Return MASInteractionState.Pressed

                    Case MASVisualInteractionState.Selected
                        Return MASInteractionState.Selected

                    Case MASVisualInteractionState.Focused
                        Return MASInteractionState.Focused

                    Case MASVisualInteractionState.Disabled
                        Return MASInteractionState.Disabled

                    Case Else
                        Return MASInteractionState.Enabled
                End Select
            End Function
            Private Sub DrawFallbackHamburger(canvas As SKCanvas,
                                              ctx As MASThemeContext,
                                              dpi As Single,
                                              r As SKRect,
                                              isEnabled As Boolean)

                Dim menuTheme As IMASMenuTheme = If(ctx.Theme Is Nothing, Nothing, MASAppThemeContracts.Families(ctx.Theme).Menu)
                If menuTheme Is Nothing Then Return

                Dim itemText As MASMenuItemTextTheme = menuTheme.ItemText

                Dim iconCx As Single = PixelSnap.SnapHalf(r.MidX)
                Dim iconCy As Single = PixelSnap.SnapHalf(r.MidY)
                Dim w As Single = r.Width * TopBarTokens.MenuButtonHamburgerWidthRatio
                Dim gap As Single = Math.Max(TopBarTokens.MenuButtonHamburgerGapMin, TopBarTokens.MenuButtonHamburgerGap * dpi)

                _icon.StrokeWidth = Math.Max(TopBarTokens.MenuButtonHamburgerStrokeMin, TopBarTokens.MenuButtonHamburgerStroke * dpi)
                _icon.Color = If(isEnabled, itemText.Icon, itemText.IconMuted)
                _icon.Shader = Nothing

                canvas.DrawLine(PixelSnap.SnapHalf(iconCx - w), PixelSnap.SnapHalf(iconCy - gap), PixelSnap.SnapHalf(iconCx + w), PixelSnap.SnapHalf(iconCy - gap), _icon)
                canvas.DrawLine(PixelSnap.SnapHalf(iconCx - w), PixelSnap.SnapHalf(iconCy), PixelSnap.SnapHalf(iconCx + w), PixelSnap.SnapHalf(iconCy), _icon)
                canvas.DrawLine(PixelSnap.SnapHalf(iconCx - w), PixelSnap.SnapHalf(iconCy + gap), PixelSnap.SnapHalf(iconCx + w), PixelSnap.SnapHalf(iconCy + gap), _icon)
            End Sub

            Public Sub Dispose() Implements IDisposable.Dispose
                If _disposed Then Return
                _disposed = True

                Try
                    _fill.Dispose()
                Catch masCaughtException7 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException7)
                End Try
                Try
                    _stroke.Dispose()
                Catch masCaughtException8 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException8)
                End Try
                Try
                    _icon.Dispose()
                Catch masCaughtException9 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException9)
                End Try
                Try
                    _lottie.Dispose()
                Catch masCaughtException10 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException10)
                End Try
            End Sub

        End Class

    End Class

End Namespace
