Fix labels, lists and other tweaks

This commit is contained in:
2026-01-24 02:21:56 +00:00
parent 5415c71e9e
commit 7830356f24
10 changed files with 100 additions and 27 deletions

View File

@@ -761,12 +761,17 @@ public class LinuxApplication : IDisposable
if (view is SkiaNavigationPage navPage && navPage.CurrentPage != null)
{
RefreshViewTheme(navPage.CurrentPage);
navPage.Invalidate(); // Force redraw of navigation page
}
// Special handling for ContentPage - it stores content in Content property
if (view is SkiaPage page && page.Content != null)
if (view is SkiaPage page)
{
RefreshViewTheme(page.Content);
page.Invalidate(); // Force redraw to pick up theme-aware background
if (page.Content != null)
{
RefreshViewTheme(page.Content);
}
}
// Recursively process children

View File

@@ -558,7 +558,7 @@ public class SkiaButton : SkiaView, IButtonController
// This ensures buttons are visible even without explicit styling
if (!hasExplicitBackground)
{
bgColor = SkiaTheme.Gray200SK; // Default button background
bgColor = SkiaTheme.ButtonBackgroundSK; // Theme-aware default button background
}
bool hasBackground = hasExplicitBackground ? bgColor.Alpha > 0 : true;
@@ -688,8 +688,8 @@ public class SkiaButton : SkiaView, IButtonController
}
else
{
// Default button (gray background) - use dark text for contrast
textColor = SkiaTheme.Gray800SK;
// Default button - use theme-appropriate text color for contrast
textColor = SkiaTheme.CurrentTextSK;
}
if (!IsEnabled)

View File

@@ -289,7 +289,10 @@ public class SkiaCheckBox : SkiaView
// Get colors as SKColor
var colorSK = ToSKColor(Color);
var checkColorSK = ToSKColor(CheckColor);
var uncheckedBoxColorSK = ToSKColor(UncheckedBoxColor);
// Use theme-aware color for unchecked box if default white
var uncheckedBoxColorSK = UncheckedBoxColor == Colors.White
? SkiaTheme.CurrentSurfaceSK
: ToSKColor(UncheckedBoxColor);
var borderColorSK = ToSKColor(BorderColor);
var disabledColorSK = ToSKColor(DisabledColor);
var hoveredBorderColorSK = ToSKColor(HoveredBorderColor);

View File

@@ -429,14 +429,19 @@ public class SkiaCollectionView : SkiaItemsView
rawHeight = ItemHeight;
}
var measuredHeight = Math.Max(rawHeight, ItemHeight);
if (!_itemHeights.TryGetValue(index, out var cachedHeight) || Math.Abs(cachedHeight - measuredHeight) > 1f)
// Store the actual measured height for row sizing
var cellHeight = Math.Max(rawHeight, ItemHeight);
if (!_itemHeights.TryGetValue(index, out var cachedHeight) || Math.Abs(cachedHeight - cellHeight) > 1f)
{
_itemHeights[index] = measuredHeight;
_itemHeights[index] = cellHeight;
_heightsChangedDuringDraw = true;
}
var actualBounds = new SKRect(bounds.Left, bounds.Top, bounds.Right, bounds.Top + measuredHeight);
// Vertically center the content within the cell bounds
// Use rawHeight (actual content height) for centering, not cellHeight
var contentHeight = Math.Min(rawHeight, bounds.Height);
var verticalOffset = Math.Max(0, (bounds.Height - contentHeight) / 2);
var actualBounds = new SKRect(bounds.Left, bounds.Top + verticalOffset, bounds.Right, bounds.Top + verticalOffset + contentHeight);
itemView.Arrange(new Rect(actualBounds.Left, actualBounds.Top, actualBounds.Width, actualBounds.Height));
itemView.Draw(canvas);

View File

@@ -149,10 +149,12 @@ public class SkiaItemsView : SkiaView
/// <summary>
/// Gets the height for a specific item, using cached height or default.
/// Always returns at least ItemHeight to allow vertical centering of smaller content.
/// </summary>
protected float GetItemHeight(int index)
{
return _itemHeights.TryGetValue(index, out var height) ? height : _itemHeight;
var cached = _itemHeights.TryGetValue(index, out var height) ? height : _itemHeight;
return Math.Max(cached, _itemHeight);
}
/// <summary>

View File

@@ -711,10 +711,6 @@ public class SkiaLabel : SkiaView
if (needsMultiLine)
{
var textBoundsDbg = new SKRect();
paint.MeasureText(displayText, ref textBoundsDbg);
if (displayText.StartsWith("Full XAML") || displayText.StartsWith("Shell nav"))
Console.WriteLine($"[Label OnDraw] '{displayText.Substring(0, Math.Min(15, displayText.Length))}' textW={textBoundsDbg.Width:F0} boundsW={contentBounds.Width:F0} LineBreakMode={LineBreakMode}");
DrawMultiLineText(canvas, paint, font, contentBounds, displayText);
}
else
@@ -1126,6 +1122,15 @@ public class SkiaLabel : SkiaView
continue;
}
// Check if the entire paragraph fits on one line - no need to wrap
// Use small tolerance to account for floating point precision
float paragraphWidth = paint.MeasureText(paragraph);
if (paragraphWidth <= maxWidth + 1.0f)
{
lines.Add(paragraph);
continue;
}
if (LineBreakMode == LineBreakMode.CharacterWrap)
{
WrapByCharacter(paragraph, paint, maxWidth, lines);
@@ -1236,9 +1241,11 @@ public class SkiaLabel : SkiaView
}
else
{
// Use advance width (paint.MeasureText return value) not bounding box width
// This must match what WrapText uses for consistency
var textBounds = new SKRect();
paint.MeasureText(displayText, ref textBounds);
width = textBounds.Width;
width = paint.MeasureText(displayText); // Advance width, not textBounds.Width
height = textBounds.Height;
// Account for character spacing

View File

@@ -454,8 +454,6 @@ public class SkiaStackLayout : SkiaLayoutView
// Horizontal stack: give each child its measured width
// Don't constrain - let content overflow if needed (parent clips)
var useWidth = childWidth;
if (child is SkiaLabel lbl)
Console.WriteLine($"[HStack Arrange] Label '{lbl.Text?.Substring(0, Math.Min(15, lbl.Text?.Length ?? 0))}' childWidth={childWidth:F0} contentWidth={contentWidth:F0} offset={offset:F0}");
// Respect child's VerticalOptions for horizontal layouts
var useHeight = Math.Min(childHeight, contentHeight);

View File

@@ -141,16 +141,24 @@ public class SkiaPage : SkiaView
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
// Draw background color
// Draw background color - use theme-aware default if not set
SKColor bgColor;
if (BackgroundColor != null && BackgroundColor != Colors.Transparent)
{
using var bgPaint = new SKPaint
{
Color = GetEffectiveBackgroundColor(),
Style = SKPaintStyle.Fill
};
canvas.DrawRect(bounds, bgPaint);
bgColor = GetEffectiveBackgroundColor();
}
else
{
// Use theme-aware page background when no explicit color is set
bgColor = SkiaTheme.CurrentPageBackgroundSK;
}
using var bgPaint = new SKPaint
{
Color = bgColor,
Style = SKPaintStyle.Fill
};
canvas.DrawRect(bounds, bgPaint);
// Draw background image if set
if (BackgroundImage != null)

View File

@@ -143,10 +143,16 @@ public class SkiaSearchBar : SkiaView
float cornerRadius = (float)CornerRadius;
float iconSize = (float)IconSize;
// Draw background
// Draw background - use theme-aware color if not explicitly set
var bgColor = SearchBarBackgroundColor.ToSKColor();
// If using default light color, check for dark mode
if (SearchBarBackgroundColor.Red > 0.9f && SearchBarBackgroundColor.Green > 0.9f && SearchBarBackgroundColor.Blue > 0.9f)
{
bgColor = SkiaTheme.InputBackgroundSK;
}
using var bgPaint = new SKPaint
{
Color = SearchBarBackgroundColor.ToSKColor(),
Color = bgColor,
IsAntialias = true,
Style = SKPaintStyle.Fill
};

View File

@@ -317,4 +317,43 @@ public static class SkiaTheme
internal static readonly SKColor DarkHoverSK = DarkHover.ToSKColor();
#endregion
#region Theme-Aware Helpers
/// <summary>
/// Returns true if dark mode is currently active.
/// </summary>
public static bool IsDarkMode => Application.Current?.UserAppTheme == AppTheme.Dark;
/// <summary>
/// Gets the appropriate button background color for the current theme.
/// </summary>
public static SKColor ButtonBackgroundSK => IsDarkMode ? DarkSurfaceSK : Gray200SK;
/// <summary>
/// Gets the appropriate text color for the current theme.
/// </summary>
public static SKColor CurrentTextSK => IsDarkMode ? DarkTextSK : TextPrimarySK;
/// <summary>
/// Gets the appropriate background color for input controls in the current theme.
/// </summary>
public static SKColor InputBackgroundSK => IsDarkMode ? DarkBackgroundSK : BackgroundWhiteSK;
/// <summary>
/// Gets the appropriate border color for the current theme.
/// </summary>
public static SKColor CurrentBorderSK => IsDarkMode ? Gray600SK : BorderLightSK;
/// <summary>
/// Gets the appropriate surface background for the current theme.
/// </summary>
public static SKColor CurrentSurfaceSK => IsDarkMode ? DarkSurfaceSK : BackgroundWhiteSK;
/// <summary>
/// Gets the appropriate page background for the current theme.
/// </summary>
public static SKColor CurrentPageBackgroundSK => IsDarkMode ? DarkBackgroundSK : BackgroundWhiteSK;
#endregion
}