Fix labels, lists and other tweaks
This commit is contained in:
@@ -761,12 +761,17 @@ public class LinuxApplication : IDisposable
|
|||||||
if (view is SkiaNavigationPage navPage && navPage.CurrentPage != null)
|
if (view is SkiaNavigationPage navPage && navPage.CurrentPage != null)
|
||||||
{
|
{
|
||||||
RefreshViewTheme(navPage.CurrentPage);
|
RefreshViewTheme(navPage.CurrentPage);
|
||||||
|
navPage.Invalidate(); // Force redraw of navigation page
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for ContentPage - it stores content in Content property
|
// 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
|
// Recursively process children
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ public class SkiaButton : SkiaView, IButtonController
|
|||||||
// This ensures buttons are visible even without explicit styling
|
// This ensures buttons are visible even without explicit styling
|
||||||
if (!hasExplicitBackground)
|
if (!hasExplicitBackground)
|
||||||
{
|
{
|
||||||
bgColor = SkiaTheme.Gray200SK; // Default button background
|
bgColor = SkiaTheme.ButtonBackgroundSK; // Theme-aware default button background
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasBackground = hasExplicitBackground ? bgColor.Alpha > 0 : true;
|
bool hasBackground = hasExplicitBackground ? bgColor.Alpha > 0 : true;
|
||||||
@@ -688,8 +688,8 @@ public class SkiaButton : SkiaView, IButtonController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Default button (gray background) - use dark text for contrast
|
// Default button - use theme-appropriate text color for contrast
|
||||||
textColor = SkiaTheme.Gray800SK;
|
textColor = SkiaTheme.CurrentTextSK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
|
|||||||
@@ -289,7 +289,10 @@ public class SkiaCheckBox : SkiaView
|
|||||||
// Get colors as SKColor
|
// Get colors as SKColor
|
||||||
var colorSK = ToSKColor(Color);
|
var colorSK = ToSKColor(Color);
|
||||||
var checkColorSK = ToSKColor(CheckColor);
|
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 borderColorSK = ToSKColor(BorderColor);
|
||||||
var disabledColorSK = ToSKColor(DisabledColor);
|
var disabledColorSK = ToSKColor(DisabledColor);
|
||||||
var hoveredBorderColorSK = ToSKColor(HoveredBorderColor);
|
var hoveredBorderColorSK = ToSKColor(HoveredBorderColor);
|
||||||
|
|||||||
@@ -429,14 +429,19 @@ public class SkiaCollectionView : SkiaItemsView
|
|||||||
rawHeight = ItemHeight;
|
rawHeight = ItemHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
var measuredHeight = Math.Max(rawHeight, ItemHeight);
|
// Store the actual measured height for row sizing
|
||||||
if (!_itemHeights.TryGetValue(index, out var cachedHeight) || Math.Abs(cachedHeight - measuredHeight) > 1f)
|
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;
|
_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.Arrange(new Rect(actualBounds.Left, actualBounds.Top, actualBounds.Width, actualBounds.Height));
|
||||||
itemView.Draw(canvas);
|
itemView.Draw(canvas);
|
||||||
|
|
||||||
|
|||||||
@@ -149,10 +149,12 @@ public class SkiaItemsView : SkiaView
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the height for a specific item, using cached height or default.
|
/// 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>
|
/// </summary>
|
||||||
protected float GetItemHeight(int index)
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -711,10 +711,6 @@ public class SkiaLabel : SkiaView
|
|||||||
|
|
||||||
if (needsMultiLine)
|
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);
|
DrawMultiLineText(canvas, paint, font, contentBounds, displayText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1126,6 +1122,15 @@ public class SkiaLabel : SkiaView
|
|||||||
continue;
|
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)
|
if (LineBreakMode == LineBreakMode.CharacterWrap)
|
||||||
{
|
{
|
||||||
WrapByCharacter(paragraph, paint, maxWidth, lines);
|
WrapByCharacter(paragraph, paint, maxWidth, lines);
|
||||||
@@ -1236,9 +1241,11 @@ public class SkiaLabel : SkiaView
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Use advance width (paint.MeasureText return value) not bounding box width
|
||||||
|
// This must match what WrapText uses for consistency
|
||||||
var textBounds = new SKRect();
|
var textBounds = new SKRect();
|
||||||
paint.MeasureText(displayText, ref textBounds);
|
paint.MeasureText(displayText, ref textBounds);
|
||||||
width = textBounds.Width;
|
width = paint.MeasureText(displayText); // Advance width, not textBounds.Width
|
||||||
height = textBounds.Height;
|
height = textBounds.Height;
|
||||||
|
|
||||||
// Account for character spacing
|
// Account for character spacing
|
||||||
|
|||||||
@@ -454,8 +454,6 @@ public class SkiaStackLayout : SkiaLayoutView
|
|||||||
// Horizontal stack: give each child its measured width
|
// Horizontal stack: give each child its measured width
|
||||||
// Don't constrain - let content overflow if needed (parent clips)
|
// Don't constrain - let content overflow if needed (parent clips)
|
||||||
var useWidth = childWidth;
|
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
|
// Respect child's VerticalOptions for horizontal layouts
|
||||||
var useHeight = Math.Min(childHeight, contentHeight);
|
var useHeight = Math.Min(childHeight, contentHeight);
|
||||||
|
|||||||
@@ -141,16 +141,24 @@ public class SkiaPage : SkiaView
|
|||||||
|
|
||||||
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
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)
|
if (BackgroundColor != null && BackgroundColor != Colors.Transparent)
|
||||||
{
|
{
|
||||||
using var bgPaint = new SKPaint
|
bgColor = GetEffectiveBackgroundColor();
|
||||||
{
|
|
||||||
Color = GetEffectiveBackgroundColor(),
|
|
||||||
Style = SKPaintStyle.Fill
|
|
||||||
};
|
|
||||||
canvas.DrawRect(bounds, bgPaint);
|
|
||||||
}
|
}
|
||||||
|
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
|
// Draw background image if set
|
||||||
if (BackgroundImage != null)
|
if (BackgroundImage != null)
|
||||||
|
|||||||
@@ -143,10 +143,16 @@ public class SkiaSearchBar : SkiaView
|
|||||||
float cornerRadius = (float)CornerRadius;
|
float cornerRadius = (float)CornerRadius;
|
||||||
float iconSize = (float)IconSize;
|
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
|
using var bgPaint = new SKPaint
|
||||||
{
|
{
|
||||||
Color = SearchBarBackgroundColor.ToSKColor(),
|
Color = bgColor,
|
||||||
IsAntialias = true,
|
IsAntialias = true,
|
||||||
Style = SKPaintStyle.Fill
|
Style = SKPaintStyle.Fill
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -317,4 +317,43 @@ public static class SkiaTheme
|
|||||||
internal static readonly SKColor DarkHoverSK = DarkHover.ToSKColor();
|
internal static readonly SKColor DarkHoverSK = DarkHover.ToSKColor();
|
||||||
|
|
||||||
#endregion
|
#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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user