* Move "AdditionalFiles Include="@(AvaloniaXaml)"" to common AvaloniaBuildTasks
* Add AdditionalFiles Include="@(AvaloniaResource)"
* Add a comment
* Add extra CompilerVisibleProperty
* Rename _InjectAvaloniaAdditionalFiles, make it more specific...
* Do not duplicate AdditionalFiles with AvaloniaXaml AvaloniaResource
* Support Value pattern on editable ComboBox automation peer
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Add CalendarDayButtonAutomationPeer with SelectionItem pattern
* Guard day button selection against None mode and blackout days, add peer tests
* Use NotNullWhen instead of null-forgiving out parameter
* Implement IToggleProvider on MenuItemAutomationPeer
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Add ShouldRenderOnUIThread//Fps options to the AvaloniaHeadlessPlatformOptions, allowing SleepLoopRenderTimer to be enabled
* Don't throw from ForceRenderTimerTick
* Run DispatcherFrame on RequestCommitAsync, when HeadlessRenderTimer is not available
* This method apparently was public
* Fix BiDi correctness across TextCollapsingProperties implementations
Audit and fix logical-vs-visual ordering bugs in every text-collapsing
implementation and the helpers they share, so trimming always operates in
logical order (the consumer re-runs the BiDi reorderer in FinalizeLine, so
pre-applying visual order would be reordered a second time).
- LogicalTextRunEnumerator returned the first run on every MoveNext when
_indexedTextRuns was null.
- TextLeadingPrefixCharacterEllipsis: walked visual-order runs while splitting
at logical offsets, guarded the wrong field in the ctor, hardcoded
LeftToRight for the symbol, appended the suffix in reverse-logical order, and
mis-tracked the width budget across multi-run lines.
- TextPathSegmentEllipsis: crashed via Split(0) in the fallback path, and
measured RTL segments as ~0 width (it assumed ascending cluster ids).
- ShapedTextRun.TryMeasureCharacters/Backwards returned visual-order counts for
RTL runs.
ShapedBuffer gains logical-order, cluster-cache-backed width helpers
(GetCharRangeWidth, FindLeading/FindTrailingCharCountWithinWidth) that are
safe in the one-char-per-cluster simple mode (null _clusterStartChars). Adds a
BiDi characterization test suite and documents the logical-order Collapse
contract.
* Align text width comparisons to epsilon-tolerant MathUtilities
Several width-fit comparisons in the measure/collapse path used raw operators
with no epsilon, while the cluster-cache hot path (MeasureCharactersThatFit)
and the ShapedTextRun branch of MeasureLength already went through
MathUtilities. Replace the raw float comparisons with their epsilon-aware
equivalents (<= -> LessThanOrClose, < -> LessThan, > -> GreaterThan) in
ShapedBuffer.FindLeading/FindTrailingCharCountWithinWidth,
TextPathSegmentEllipsis, and TextLeadingPrefixCharacterEllipsis.
Also unify the DrawableTextRun branch of TextFormatterImpl.MeasureLength with
the ShapedTextRun branch (>= -> GreaterThan) so the "whole run fits" boundary
is identical for both run types: a run fits when width <= remaining (tolerant)
and overflows only when strictly greater.
Integer char-offset/index comparisons and the availableWidth <= 0 positivity
guards are intentionally left as raw comparisons.
* Cleanup some comments
* Fix Split(0) crash in TextLeadingPrefixCharacterEllipsis suffix collection
When a logical-tail run fits entirely within the remaining suffix budget,
TryMeasureCharactersBackwards returns suffixCount == run.Length, so
Split(run.Length - suffixCount) evaluated to Split(0), which throws
ArgumentOutOfRangeException. This was reachable on multi-run lines whose
post-split tail contains a small later run that fully fits.
Guard the split on splitAt > 0 and use the whole run as-is when it fully fits
(dropping it only when suffixCount == 0) — the same boundary pattern already
applied to TextPathSegmentEllipsis. Add a multi-run regression test.
* Remove duplicate ShapedBuffer.MeasureCharactersThatFit
MeasureCharactersThatFit and FindLeadingCharCountWithinWidth ran the
identical cluster-prefix binary search; the only functional difference
was the unused `out widthConsumed`. Drop the former and repoint its
callers (TextFormatterImpl.MeasureLength and the benchmark) at
FindLeadingCharCountWithinWidth, which also unifies the availableWidth<=0
guard. The one test that asserted the consumed width now recovers it via
GetCharRangeWidth(0, fit).
Also tidy GetCharRangeWidth's simple-mode bounds to use Math.Clamp.
* Make font matching inside the FontCollectionBase implementation culture aware
* Aligning `RefineWithCulture` with major text-stack implementations
* Revert submodule change
* Fix BuildTypefaceWithSynthesis
* Add a better unit test coverage for TryMatchCharacter that covers the tiered algorithm
* Add some comments to MetaTable parsing
Use Span helpers
Optimize ScriptExtensions.data
Change to the latest version of Tmds.DBus.Protocol and use the project source generator.
Fix StatusNotifierItem Status property to use "Active" value.
Fix "watch after request" race for portal requests.
Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com>
No implementation of `BindingBase` implements `IObservable<T>` any more, so the `TryBindStyledPropertyUntyped` and `TryBindDirectPropertyUntyped` methods always returned null.
Remove them, and move the `IsReadOnly` check into a separate method in the spirit of extracting non-generic code from frequently used generic types.
* Add failing test for cursor when pointer changes
* Introduce PresentationSource.CursorElement
* Update cursor on capture change
* Set correct cursor when capture is released
* Recompute pointer-over element immediately when capture changes
* perf(text): O(1) width queries on ShapedBuffer, cached LB-class lookup
- ShapedBuffer: lazy, pooled cluster-prefix cache shared across Split;
adds TotalGlyphAdvance / MeasureCharactersThatFit.
- GlyphRun/TextFormatterImpl/Skia GlyphRunImpl: read the cache, drop
duplicate scans; SplitTextRuns now returns firstLength via out.
- LineBreakEnumerator: cache Next/PreviousClass per advance; remove
unused LineBreakPairTable.
- Tests + benchmarks for the new paths and a dotnet-trace harness.
~1.32x faster / -5% alloc on the emoji-wrap micro-benchmark; no
observable behaviour or public API change.
* perf(TextFormatting): inline LineBreakEnumerator rule dispatch
Replace the BreakUnitDelegate[] s_rules array dispatch in
LineBreakEnumerator.ExecuteRules with a sequence of direct static
calls and a `goto Done` early-exit. This removes 42 indirect calls
per codepoint and lets the JIT reason across rule boundaries, which
in turn makes [MethodImpl(AggressiveInlining)] meaningful — JIT
cannot inline through delegate.Invoke, so the attribute was a no-op
in the previous shape.
Selectively apply AggressiveInlining to the small single-condition
rules (LB03, LB04, LB06, LB07, LB08a, LB11–LB15d, LB18, LB20,
LB21b, LB22, LB29, LB31) and let the JIT decide on the larger ones
to avoid bloating the merged ExecuteRules.
The static BreakUnitDelegate[] s_rules array is removed.
Benchmarks (BDN default job, --inProcess, N=13–22, rel. StdDev <1.5%):
UnicodeBreakEnumeratorBenchmark.LineBreakEnumerator_Sequence
Ascii 154.6 µs -> 24.84 µs (6.22x)
Bmp 190.2 µs -> 32.81 µs (5.80x)
Supplementary 227.6 µs -> 37.56 µs (6.06x)
TextLayoutProfile.BuildEmojisWrapped
Before (branch, pre-inline) 804.5 µs / 570.15 KB
After (branch, post-inline) 547.0 µs / 570.15 KB (-32.0%)
vs upstream/master (1061 µs) -> ~1.94x total speedup
No allocation change; CPU-only dispatch reshape. All
LineBreakEnumerator unit tests pass (5/5).
* Refactor ShapedBuffer to share pool storage via IRef + generation counter
Wrap the ArrayPool-rented glyph and cluster-cache arrays in a small
PooledArray<T> disposable and expose them through IRef<T>. Split children
and WithBidiLevel aliases now Clone() the refs instead of borrowing raw
pool arrays, so the backing storage survives until every sibling has
been disposed - eliminating the UAF risk that existed when a parent was
disposed before its children.
Add a per-glyph-holder generation counter (Volatile.Read / Interlocked
.Increment). The indexer setter bumps the counter on every write, and
EnsureClusterCache compares its recorded generation against the holder's
current value, rebuilding on mismatch. This lets us drop the previous
"no mutation after Split/WithBidiLevel" contract: mutations performed
through any sibling now propagate to the others' caches transparently.
Dispose is made idempotent via a _disposed guard so overlapping cache
eviction and TextLine teardown only release the IRefs once.
Adds ShapedBufferSharedStorageTests covering sibling lifetime,
Dispose idempotency, and generation-driven cache invalidation across
Split children and WithBidiLevel aliases.
* test(ShapedBuffer): cover cluster-cache sharing across Split/WithBidiLevel aliases
Add a regression test hook (`ClusterPrefix`) exposing the
backing cluster-prefix array reference, plus two tests that mutate a
parent buffer before aliasing it and assert the alias reuses the
parent's pooled prefix array instead of rebuilding. Guards against
forgetting to propagate `_cacheGeneration` to alias buffers, which
would silently defeat the cached-split fast path.
* Correctly use MathUtilities.LessThanOrClose
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* fix: raise SelectionChanged event on collection Reset when items are deselected
When a collection bound to a SelectingItemsControl (e.g. ListBox) was
cleared via NotifyCollectionChangedAction.Reset, the SelectionChanged
event was not raised despite the selection being lost.
The root cause was in InternalSelectionModel.OnSourceReset: the base
SelectionModel.OnSourceReset() directly reset _selectedIndex to -1
before any Operation could capture the old selection state. The
subsequent SyncFromSelectedItems created an Operation that saw no
change (old and new both -1), so CommitOperation never fired
SelectionChanged.
The fix snapshots _writableSelectedItems before sync, diffs against
the post-sync state to find items that were actually lost (not merely
re-selected at a new index after reorder), and injects them as
DeselectedItems on the pending Operation — following the same pattern
used by OnSelectionRemoved for individual item removals.
Fixes#20897
* fix: use multiset diff to report lost duplicate selections on Reset
The Reset diff in InternalSelectionModel used a HashSet to detect
which previously-selected items were still present after sync. Selection
allows duplicates (same instance or equal items at multiple indices),
so set semantics collapsed duplicates into one entry and under-reported
deselections when only some occurrences were lost.
Track counts per item plus a null counter and decrement per match, so
RemovedItems reflects the actual number of lost selections.
Adds a duplicate-items Reset test covering the regression.
* fix: raise SelectionChanged for reset-lost selection
ListBox and other SelectingItemsControl callers did not receive SelectionChanged when a Reset cleared the selected items. The selection model reports this path through LostSelection, but the control only used that callback for AlwaysSelected recovery.
Track the last selected items at the control boundary, capture that snapshot for Reset notifications, and raise the routed SelectionChanged event when LostSelection commits during that reset. This avoids diffing reset contents while preserving the removed-items payload for clear/reset-to-empty cases.
* fix: address review feedback on SelectionChanged Reset snapshot
- Replace per-change ToArray() snapshot with persistent List<object?> to
avoid allocations on every selection change (review: MrJul).
- Read snapshot in PreCollectionChanged instead of Selection.SelectedItems
because the source is already empty by the time Reset fires.
- Align LostSelection event-raising with SelectionChanged path: use
BuildEventRoute + HasHandlers guard to avoid allocating args when
no handlers are attached (review: copilot).
- Harden existing Reset tests to Assert.Single to catch double-fire.
* fix: consolidate SelectionChanged raising and defend against stale snapshot
- Extract RaiseSelectionChanged helper so both the normal
(SelectionChanged) and reset (LostSelection) paths share the same
BuildEventRoute/HasHandlers guard and SelectionChangedEventArgs
construction (review: copilot).
- Move _selectedItemsBeforeReset clear outside the conditional in
OnSelectionModelLostSelection so the field is always nulled after
LostSelection, preventing accidental reuse (review: copilot).
- Add comment documenting the snapshot lifecycle in
OnItemsViewPreCollectionChanged.
* perf: defer SelectionChangedEventArgs allocations until handlers are confirmed
SelectionChangedEventArgs materialized arrays via ToArray() at the call site
before checking whether the routed event had any registered handlers. This
allocated needlessly in the common case of no external subscribers.
The event items (IReadOnlyList<object?>) already implement IList via
ReadOnlySelectionListBase. RaiseSelectionChanged now accepts
IReadOnlyList<object?> and casts to IList inside the HasHandlers gate,
falling back to ToArray() only for non-IList enumerables.
* add failing test for RenderTargetBitmap_DropShadowEffect
* Fix immidiateRenderer with Effect wasn't working as expected
* yet another render test
* XML comments for newly added members
* Address Copilot review
* Add another unit test to ensure the recent changes
don't get lost at some point in time
* address review
* Apply Review suggestion
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* inline effectPadding
* implement feedback for better bounds handling
* Update src/Avalonia.Base/Media/DrawingGroup.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* anohter minor fix
* Address PR #20790 review comments
- Fix RenderDataEffectNode.Bounds to return null when no children exist,
preventing empty effect nodes from incorrectly reporting non-null bounds
and causing render artifacts due to incorrect dirty rects.
Bounds now inflates child bounds by the effect output padding rather
than unioning with BoundsRect.
- Move effect output padding inflation from callers into DrawingContext
implementations (PlatformDrawingContext, RenderDataDrawingContext) so
callers pass content bounds to PushEffect and the API handles inflation
internally. Remove pre-inflation from ImmediateRenderer.
- Fix DrawingGroup.DrawCore to pass effectBounds (inflated) to
PushOpacityMask when an Effect is set, so the opacity mask covers
the full effect output region (e.g. shadow/blur extending beyond
visual bounds).
- Fix DrawingGroup.GetBounds to inflate EffectBounds (which now stores
content bounds) by the effect output padding.
- Fix existing compile error: Rect.IsEmpty() is a method, not property.
- Add failing tests for each of the above before fixing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix DrawingGroup.GetBounds() incorrectly inflating bounds with effect output padding
GetBounds() should return content/geometric bounds only, matching WPF behavior.
Inflating by GetEffectOutputPadding() caused DrawingImage to shift its coordinate
origin by the effect's extent (e.g. a 3.5px shadow offset would displace all
content by 3.5px), breaking Should_Render_DrawingGroup_With_Effect render test.
Effects render additively outside the content area and must not affect the
coordinate system established by GetBounds().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* [Automation] Add EmbeddableControlRootAutomationPeer tests
* [Automation] Implement IRootProvider on EmbeddableControlRootAutomationPeer
Without IRootProvider, AutomationNode.Create falls back to the plain
AutomationNode which doesn't implement IRawElementProviderFragmentRoot
and returns null from GetHostRawElementProvider. As a result,
UiaReturnRawElementProvider responds with E_FAIL for the WM_GETOBJECT
sent to the embedded HWND, and the Avalonia automation tree is invisible
to UIA clients (Inspect.exe, Narrator, FlaUI etc.) when the control is
hosted via WinFormsAvaloniaControlHost.
* Add new CDPicker props for custom text conversion
* Use TextConverter if not null for date parsing
* Fix comment on CustomDateFormatString
* Add unit test for custom date parsing
* CDPicker: When text set ensure right format
* Add test for invalid date input
* Fix failure on new test for invalid date input
* Swap Convert and ConvertBack
See review comment: https://github.com/AvaloniaUI/Avalonia/pull/21193#discussion_r3265891798
* Add failing test for TextConverter.Convert
* Tweak Convert test based on InvokeAsync used in prop update
* Use TextConverter if avail in DateTimeToString
https://github.com/AvaloniaUI/Avalonia/pull/21193#discussion_r3265887941
* Add clarifying comment
* Rename converter for clarity
* Fix failing tests due to DateTimeToString change
* Update docs for CalendarDatePicker.TextConverter
https://github.com/AvaloniaUI/Avalonia/pull/21193#discussion_r3265904591
---------
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
* Update XamlX
* XAML compiler: cache AvaloniaXamlIlWellKnownTypes per compilation
* XAML compiler: use more well known types
* XAML compiler: replace IXamlType.FullName usages
* Add missing AutomationPeers from built-in Controls.
* revert native menu bar peers
* Add back a thin automation peer for native menu bar.
* Use ColorChanged for ColorSpectrumAutomationPeer
* Fix NativeMenuBar/ColorSpectrum automation peer regressions
Flatten MenuItem children under NativeMenuBarAutomationPeer and raise ColorChanged when Color is set directly so ColorSpectrumAutomationPeer notifies AT clients.
* Drop NativeMenuBarAutomationPeer.GetChildrenCore override
Bisect confirmed this override caused 5 Windows IT tests (Slider, Screen, DragDrop) to fail by producing an inconsistent UIA tree where MenuItem peers list NativeMenuBar as parent while their visual parent is the inner Menu. Reverted to the thin peer that only reports ControlType=MenuBar, and dropped the now-irrelevant children assertion test.