From 7830356f24c4ce91d29923f325c3b3ed2e22c545 Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 24 Jan 2026 02:21:56 +0000 Subject: [PATCH] Fix labels, lists and other tweaks --- LinuxApplication.cs | 9 +++++++-- Views/SkiaButton.cs | 6 +++--- Views/SkiaCheckBox.cs | 5 ++++- Views/SkiaCollectionView.cs | 13 +++++++++---- Views/SkiaItemsView.cs | 4 +++- Views/SkiaLabel.cs | 17 +++++++++++----- Views/SkiaLayoutView.cs | 2 -- Views/SkiaPage.cs | 22 ++++++++++++++------- Views/SkiaSearchBar.cs | 10 ++++++++-- Views/SkiaTheme.cs | 39 +++++++++++++++++++++++++++++++++++++ 10 files changed, 100 insertions(+), 27 deletions(-) diff --git a/LinuxApplication.cs b/LinuxApplication.cs index 152caf2..a0867a9 100644 --- a/LinuxApplication.cs +++ b/LinuxApplication.cs @@ -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 diff --git a/Views/SkiaButton.cs b/Views/SkiaButton.cs index 8bdbfdb..058f41a 100644 --- a/Views/SkiaButton.cs +++ b/Views/SkiaButton.cs @@ -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) diff --git a/Views/SkiaCheckBox.cs b/Views/SkiaCheckBox.cs index d2b27fc..2ce08fc 100644 --- a/Views/SkiaCheckBox.cs +++ b/Views/SkiaCheckBox.cs @@ -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); diff --git a/Views/SkiaCollectionView.cs b/Views/SkiaCollectionView.cs index d60a39b..4219918 100644 --- a/Views/SkiaCollectionView.cs +++ b/Views/SkiaCollectionView.cs @@ -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); diff --git a/Views/SkiaItemsView.cs b/Views/SkiaItemsView.cs index c17d0b3..172d5a4 100644 --- a/Views/SkiaItemsView.cs +++ b/Views/SkiaItemsView.cs @@ -149,10 +149,12 @@ public class SkiaItemsView : SkiaView /// /// Gets the height for a specific item, using cached height or default. + /// Always returns at least ItemHeight to allow vertical centering of smaller content. /// 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); } /// diff --git a/Views/SkiaLabel.cs b/Views/SkiaLabel.cs index ceb6c70..592a16e 100644 --- a/Views/SkiaLabel.cs +++ b/Views/SkiaLabel.cs @@ -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 diff --git a/Views/SkiaLayoutView.cs b/Views/SkiaLayoutView.cs index 96d386c..6634eb0 100644 --- a/Views/SkiaLayoutView.cs +++ b/Views/SkiaLayoutView.cs @@ -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); diff --git a/Views/SkiaPage.cs b/Views/SkiaPage.cs index 22b8f14..989bb74 100644 --- a/Views/SkiaPage.cs +++ b/Views/SkiaPage.cs @@ -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) diff --git a/Views/SkiaSearchBar.cs b/Views/SkiaSearchBar.cs index 8655b2e..e6e1a6f 100644 --- a/Views/SkiaSearchBar.cs +++ b/Views/SkiaSearchBar.cs @@ -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 }; diff --git a/Views/SkiaTheme.cs b/Views/SkiaTheme.cs index 6ec70f4..6bec6b8 100644 --- a/Views/SkiaTheme.cs +++ b/Views/SkiaTheme.cs @@ -317,4 +317,43 @@ public static class SkiaTheme internal static readonly SKColor DarkHoverSK = DarkHover.ToSKColor(); #endregion + + #region Theme-Aware Helpers + + /// + /// Returns true if dark mode is currently active. + /// + public static bool IsDarkMode => Application.Current?.UserAppTheme == AppTheme.Dark; + + /// + /// Gets the appropriate button background color for the current theme. + /// + public static SKColor ButtonBackgroundSK => IsDarkMode ? DarkSurfaceSK : Gray200SK; + + /// + /// Gets the appropriate text color for the current theme. + /// + public static SKColor CurrentTextSK => IsDarkMode ? DarkTextSK : TextPrimarySK; + + /// + /// Gets the appropriate background color for input controls in the current theme. + /// + public static SKColor InputBackgroundSK => IsDarkMode ? DarkBackgroundSK : BackgroundWhiteSK; + + /// + /// Gets the appropriate border color for the current theme. + /// + public static SKColor CurrentBorderSK => IsDarkMode ? Gray600SK : BorderLightSK; + + /// + /// Gets the appropriate surface background for the current theme. + /// + public static SKColor CurrentSurfaceSK => IsDarkMode ? DarkSurfaceSK : BackgroundWhiteSK; + + /// + /// Gets the appropriate page background for the current theme. + /// + public static SKColor CurrentPageBackgroundSK => IsDarkMode ? DarkBackgroundSK : BackgroundWhiteSK; + + #endregion }