Fix layout rendering, text wrapping, and scrollbar issues

- LinuxViewRenderer: Remove redundant child rendering that caused "View already has a parent" errors
- SkiaLabel: Consider LineBreakMode.WordWrap for multi-line measurement and rendering
- SkiaScrollView: Update ContentSize in OnDraw with properly constrained measurement
- SkiaShell: Account for padding in MeasureOverride (consistent with ArrangeOverride)
- Version bump to 1.0.0-preview.4

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-27 11:20:27 -05:00
parent 02b3da17d4
commit afbf8f6782
5 changed files with 32 additions and 104 deletions

View File

@@ -429,9 +429,10 @@ public class SkiaLabel : SkiaView
bounds.Bottom - Padding.Bottom);
// Handle single line vs multiline
// Use DrawSingleLine for normal labels (MaxLines <= 1 or unlimited) without newlines
// Use DrawMultiLineWithWrapping only when MaxLines > 1 (word wrap needed) or text has newlines
bool needsMultiLine = MaxLines > 1 || Text.Contains('\n');
// Use DrawMultiLineWithWrapping when: MaxLines > 1, text has newlines, OR WordWrap is enabled
bool needsMultiLine = MaxLines > 1 || Text.Contains('\n') ||
LineBreakMode == LineBreakMode.WordWrap ||
LineBreakMode == LineBreakMode.CharacterWrap;
if (needsMultiLine)
{
DrawMultiLineWithWrapping(canvas, paint, font, contentBounds);
@@ -771,8 +772,10 @@ public class SkiaLabel : SkiaView
using var font = new SKFont(typeface, FontSize);
using var paint = new SKPaint(font);
// Use same logic as OnDraw: multiline only when MaxLines > 1 or text has newlines
bool needsMultiLine = MaxLines > 1 || Text.Contains('\n');
// Use multiline when: MaxLines > 1, text has newlines, OR WordWrap is enabled
bool needsMultiLine = MaxLines > 1 || Text.Contains('\n') ||
LineBreakMode == LineBreakMode.WordWrap ||
LineBreakMode == LineBreakMode.CharacterWrap;
if (!needsMultiLine)
{
var textBounds = new SKRect();

View File

@@ -268,8 +268,16 @@ public class SkiaScrollView : SkiaView
if (_content != null)
{
// Ensure content is measured and arranged
var availableSize = new SKSize(bounds.Width, float.PositiveInfinity);
_content.Measure(availableSize);
// Account for vertical scrollbar width to prevent horizontal scrollbar from appearing
var effectiveWidth = bounds.Width;
if (Orientation != ScrollOrientation.Horizontal && VerticalScrollBarVisibility != ScrollBarVisibility.Never)
{
// Reserve space for vertical scrollbar if content might be taller than viewport
effectiveWidth -= ScrollBarWidth;
}
var availableSize = new SKSize(effectiveWidth, float.PositiveInfinity);
// Update ContentSize with the properly constrained measurement
ContentSize = _content.Measure(availableSize);
// Apply content's margin
var margin = _content.Margin;
@@ -669,12 +677,18 @@ public class SkiaScrollView : SkiaView
case ScrollOrientation.Both:
// For Both: first measure with viewport width to get responsive layout
// Content can still exceed viewport if it has minimum width constraints
// Reserve space for vertical scrollbar to prevent horizontal scrollbar
contentWidth = float.IsInfinity(availableSize.Width) ? 800f : availableSize.Width;
if (VerticalScrollBarVisibility != ScrollBarVisibility.Never)
contentWidth -= ScrollBarWidth;
contentHeight = float.PositiveInfinity;
break;
case ScrollOrientation.Vertical:
default:
// Reserve space for vertical scrollbar to prevent horizontal scrollbar
contentWidth = float.IsInfinity(availableSize.Width) ? 800f : availableSize.Width;
if (VerticalScrollBarVisibility != ScrollBarVisibility.Never)
contentWidth -= ScrollBarWidth;
contentHeight = float.PositiveInfinity;
break;
}

View File

@@ -287,14 +287,14 @@ public class SkiaShell : SkiaLayoutView
protected override SKSize MeasureOverride(SKSize availableSize)
{
// Measure current content
// Measure current content with padding accounted for (consistent with ArrangeOverride)
if (_currentContent != null)
{
float contentTop = NavBarIsVisible ? NavBarHeight : 0;
float contentBottom = TabBarIsVisible ? TabBarHeight : 0;
var contentSize = new SKSize(
availableSize.Width,
availableSize.Height - contentTop - contentBottom);
availableSize.Width - (float)Padding.Left - (float)Padding.Right,
availableSize.Height - contentTop - contentBottom - (float)Padding.Top - (float)Padding.Bottom);
_currentContent.Measure(contentSize);
}