Verify Views files against decompiled, extract embedded types

Fixed files:
- SkiaImageButton.cs: Added SVG support with multi-path search
- SkiaNavigationPage.cs: Added LinuxApplication.IsGtkMode check
- SkiaRefreshView.cs: Added ICommand support (Command, CommandParameter)
- SkiaTemplatedView.cs: Added missing using statements

Extracted embedded types to separate files (matching decompiled pattern):
- From SkiaMenuBar.cs: MenuBarItem, MenuItem, SkiaMenuFlyout, MenuItemClickedEventArgs
- From SkiaNavigationPage.cs: NavigationEventArgs
- From SkiaTabbedPage.cs: TabItem
- From SkiaVisualStateManager.cs: SkiaVisualStateGroupList, SkiaVisualStateGroup, SkiaVisualState, SkiaVisualStateSetter
- From SkiaSwipeView.cs: SwipeItem, SwipeStartedEventArgs, SwipeEndedEventArgs
- From SkiaFlyoutPage.cs: FlyoutLayoutBehavior (already separate)
- From SkiaIndicatorView.cs: IndicatorShape (already separate)
- From SkiaBorder.cs: SkiaFrame
- From SkiaCarouselView.cs: PositionChangedEventArgs
- From SkiaCollectionView.cs: SkiaSelectionMode, ItemsLayoutOrientation
- From SkiaContentPresenter.cs: LayoutAlignment

Verified matching decompiled:
- SkiaContextMenu.cs, SkiaFlexLayout.cs, SkiaGraphicsView.cs

Build: 0 errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 17:02:39 -05:00
parent 6007b84e7a
commit f6eadaad57
171 changed files with 4208 additions and 3913 deletions

View File

@@ -262,337 +262,3 @@ public class SkiaMenuBar : SkiaView
CloseFlyout();
}
}
/// <summary>
/// Represents a top-level menu bar item.
/// </summary>
public class MenuBarItem
{
/// <summary>
/// Gets or sets the display text.
/// </summary>
public string Text { get; set; } = string.Empty;
/// <summary>
/// Gets the menu items.
/// </summary>
public List<MenuItem> Items { get; } = new();
/// <summary>
/// Gets or sets the bounds (set during rendering).
/// </summary>
internal SKRect Bounds { get; set; }
}
/// <summary>
/// Represents a menu item.
/// </summary>
public class MenuItem
{
/// <summary>
/// Gets or sets the display text.
/// </summary>
public string Text { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the keyboard shortcut text.
/// </summary>
public string? Shortcut { get; set; }
/// <summary>
/// Gets or sets whether this is a separator.
/// </summary>
public bool IsSeparator { get; set; }
/// <summary>
/// Gets or sets whether this item is enabled.
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// Gets or sets whether this item is checked.
/// </summary>
public bool IsChecked { get; set; }
/// <summary>
/// Gets or sets the icon source.
/// </summary>
public string? IconSource { get; set; }
/// <summary>
/// Gets the sub-menu items.
/// </summary>
public List<MenuItem> SubItems { get; } = new();
/// <summary>
/// Event raised when the item is clicked.
/// </summary>
public event EventHandler? Clicked;
internal void OnClicked()
{
Clicked?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// A dropdown menu flyout.
/// </summary>
public class SkiaMenuFlyout : SkiaView
{
private int _hoveredIndex = -1;
private SKRect _bounds;
/// <summary>
/// Gets or sets the menu items.
/// </summary>
public List<MenuItem> Items { get; set; } = new();
/// <summary>
/// Gets or sets the position.
/// </summary>
public SKPoint Position { get; set; }
/// <summary>
/// Gets or sets the background color.
/// </summary>
public SKColor BackgroundColor { get; set; } = SKColors.White;
/// <summary>
/// Gets or sets the text color.
/// </summary>
public SKColor TextColor { get; set; } = new SKColor(33, 33, 33);
/// <summary>
/// Gets or sets the disabled text color.
/// </summary>
public SKColor DisabledTextColor { get; set; } = new SKColor(160, 160, 160);
/// <summary>
/// Gets or sets the hover background color.
/// </summary>
public SKColor HoverBackgroundColor { get; set; } = new SKColor(230, 230, 230);
/// <summary>
/// Gets or sets the separator color.
/// </summary>
public SKColor SeparatorColor { get; set; } = new SKColor(220, 220, 220);
/// <summary>
/// Gets or sets the font size.
/// </summary>
public float FontSize { get; set; } = 13f;
/// <summary>
/// Gets or sets the item height.
/// </summary>
public float ItemHeight { get; set; } = 28f;
/// <summary>
/// Gets or sets the separator height.
/// </summary>
public float SeparatorHeight { get; set; } = 9f;
/// <summary>
/// Gets or sets the minimum width.
/// </summary>
public float MinWidth { get; set; } = 180f;
/// <summary>
/// Event raised when an item is clicked.
/// </summary>
public event EventHandler<MenuItemClickedEventArgs>? ItemClicked;
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
if (Items.Count == 0) return;
// Calculate bounds
float width = MinWidth;
float height = 0;
using var textPaint = new SKPaint
{
TextSize = FontSize,
IsAntialias = true
};
foreach (var item in Items)
{
if (item.IsSeparator)
{
height += SeparatorHeight;
}
else
{
height += ItemHeight;
var textBounds = new SKRect();
textPaint.MeasureText(item.Text, ref textBounds);
float itemWidth = textBounds.Width + 50; // Padding + icon space
if (!string.IsNullOrEmpty(item.Shortcut))
{
textPaint.MeasureText(item.Shortcut, ref textBounds);
itemWidth += textBounds.Width + 20;
}
width = Math.Max(width, itemWidth);
}
}
_bounds = new SKRect(Position.X, Position.Y, Position.X + width, Position.Y + height);
// Draw shadow
using var shadowPaint = new SKPaint
{
ImageFilter = SKImageFilter.CreateDropShadow(0, 2, 8, 8, new SKColor(0, 0, 0, 40))
};
canvas.DrawRect(_bounds, shadowPaint);
// Draw background
using var bgPaint = new SKPaint
{
Color = BackgroundColor,
Style = SKPaintStyle.Fill
};
canvas.DrawRect(_bounds, bgPaint);
// Draw border
using var borderPaint = new SKPaint
{
Color = new SKColor(200, 200, 200),
Style = SKPaintStyle.Stroke,
StrokeWidth = 1
};
canvas.DrawRect(_bounds, borderPaint);
// Draw items
float y = _bounds.Top;
textPaint.Color = TextColor;
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
if (item.IsSeparator)
{
float separatorY = y + SeparatorHeight / 2;
using var sepPaint = new SKPaint { Color = SeparatorColor, StrokeWidth = 1 };
canvas.DrawLine(_bounds.Left + 8, separatorY, _bounds.Right - 8, separatorY, sepPaint);
y += SeparatorHeight;
}
else
{
var itemBounds = new SKRect(_bounds.Left, y, _bounds.Right, y + ItemHeight);
// Draw hover background
if (i == _hoveredIndex && item.IsEnabled)
{
using var hoverPaint = new SKPaint { Color = HoverBackgroundColor, Style = SKPaintStyle.Fill };
canvas.DrawRect(itemBounds, hoverPaint);
}
// Draw check mark
if (item.IsChecked)
{
using var checkPaint = new SKPaint
{
Color = item.IsEnabled ? TextColor : DisabledTextColor,
TextSize = FontSize,
IsAntialias = true
};
canvas.DrawText("✓", _bounds.Left + 8, y + ItemHeight / 2 + 5, checkPaint);
}
// Draw text
textPaint.Color = item.IsEnabled ? TextColor : DisabledTextColor;
canvas.DrawText(item.Text, _bounds.Left + 28, y + ItemHeight / 2 + 5, textPaint);
// Draw shortcut
if (!string.IsNullOrEmpty(item.Shortcut))
{
textPaint.Color = DisabledTextColor;
var shortcutBounds = new SKRect();
textPaint.MeasureText(item.Shortcut, ref shortcutBounds);
canvas.DrawText(item.Shortcut, _bounds.Right - shortcutBounds.Width - 12, y + ItemHeight / 2 + 5, textPaint);
}
// Draw submenu arrow
if (item.SubItems.Count > 0)
{
canvas.DrawText("▸", _bounds.Right - 16, y + ItemHeight / 2 + 5, textPaint);
}
y += ItemHeight;
}
}
}
public override SkiaView? HitTest(float x, float y)
{
if (_bounds.Contains(x, y))
{
return this;
}
return null;
}
public override void OnPointerMoved(PointerEventArgs e)
{
if (!_bounds.Contains(e.X, e.Y))
{
_hoveredIndex = -1;
Invalidate();
return;
}
float y = _bounds.Top;
int newHovered = -1;
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
float itemHeight = item.IsSeparator ? SeparatorHeight : ItemHeight;
if (e.Y >= y && e.Y < y + itemHeight && !item.IsSeparator)
{
newHovered = i;
break;
}
y += itemHeight;
}
if (newHovered != _hoveredIndex)
{
_hoveredIndex = newHovered;
Invalidate();
}
}
public override void OnPointerPressed(PointerEventArgs e)
{
if (_hoveredIndex >= 0 && _hoveredIndex < Items.Count)
{
var item = Items[_hoveredIndex];
if (item.IsEnabled && !item.IsSeparator)
{
item.OnClicked();
ItemClicked?.Invoke(this, new MenuItemClickedEventArgs(item));
e.Handled = true;
}
}
}
}
/// <summary>
/// Event args for menu item clicked.
/// </summary>
public class MenuItemClickedEventArgs : EventArgs
{
public MenuItem Item { get; }
public MenuItemClickedEventArgs(MenuItem item)
{
Item = item;
}
}