diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 741570061b..4a7a329fc6 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -42,6 +42,7 @@
"src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj",
+ "src\\tools\\PublicAnalyzers\\Avalonia.Analyzers.csproj",
"tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
"tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
"tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
@@ -61,4 +62,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
]
}
-}
\ No newline at end of file
+}
diff --git a/Avalonia.sln b/Avalonia.sln
index 525e01c891..56847bae31 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -231,7 +231,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser.Blaz
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater", "src\Avalonia.Controls.ItemsRepeater\Avalonia.Controls.ItemsRepeater.csproj", "{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}"
EndProject
@@ -548,7 +555,6 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.Build.0 = Release|Any CPU
- {C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Release|Any CPU.Build.0 = Release|Any CPU
@@ -560,6 +566,10 @@ Global
{F4E36AA8-814E-4704-BC07-291F70F45193}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.ActiveCfg = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.Build.0 = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -626,6 +636,7 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props
index 14e4f6a563..7d021d051f 100644
--- a/build/DevAnalyzers.props
+++ b/build/DevAnalyzers.props
@@ -5,5 +5,10 @@
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
SetTargetFramework="TargetFramework=netstandard2.0"/>
+
diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props
index 3b14f0ce12..ac78d9c739 100644
--- a/samples/Directory.Build.props
+++ b/samples/Directory.Build.props
@@ -6,4 +6,5 @@
11
+
diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs
index 622181dba0..a37fa6fd32 100644
--- a/src/Avalonia.Base/Media/DrawingContext.cs
+++ b/src/Avalonia.Base/Media/DrawingContext.cs
@@ -361,11 +361,12 @@ namespace Avalonia.Media
/// Pushes an opacity value.
///
/// The opacity.
+ /// The bounds.
/// A disposable used to undo the opacity.
- public PushedState PushOpacity(double opacity)
+ public PushedState PushOpacity(double opacity, Rect bounds)
//TODO: Eliminate platform-specific push opacity call
{
- PlatformImpl.PushOpacity(opacity);
+ PlatformImpl.PushOpacity(opacity, bounds);
return new PushedState(this, PushedState.PushedStateType.Opacity);
}
diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs
index b7abda2c61..7b02649b6c 100644
--- a/src/Avalonia.Base/Media/DrawingGroup.cs
+++ b/src/Avalonia.Base/Media/DrawingGroup.cs
@@ -74,10 +74,12 @@ namespace Avalonia.Media
public override void Draw(DrawingContext context)
{
+ var bounds = GetBounds();
+
using (context.PushPreTransform(Transform?.Value ?? Matrix.Identity))
- using (context.PushOpacity(Opacity))
+ using (context.PushOpacity(Opacity, bounds))
using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default)
- using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, GetBounds()) : default)
+ using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, bounds) : default)
{
foreach (var drawing in Children)
{
@@ -284,7 +286,7 @@ namespace Avalonia.Media
throw new NotImplementedException();
}
- public void PushOpacity(double opacity)
+ public void PushOpacity(double opacity, Rect bounds)
{
throw new NotImplementedException();
}
diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs
index 732f1957d0..07fd2d56fa 100644
--- a/src/Avalonia.Base/Media/IImageBrush.cs
+++ b/src/Avalonia.Base/Media/IImageBrush.cs
@@ -12,6 +12,6 @@ namespace Avalonia.Media
///
/// Gets the image to draw.
///
- IBitmap Source { get; }
+ IBitmap? Source { get; }
}
}
diff --git a/src/Avalonia.Base/Media/ImageBrush.cs b/src/Avalonia.Base/Media/ImageBrush.cs
index 2f2a0fb627..718ebf1686 100644
--- a/src/Avalonia.Base/Media/ImageBrush.cs
+++ b/src/Avalonia.Base/Media/ImageBrush.cs
@@ -11,8 +11,8 @@ namespace Avalonia.Media
///
/// Defines the property.
///
- public static readonly StyledProperty SourceProperty =
- AvaloniaProperty.Register(nameof(Source));
+ public static readonly StyledProperty SourceProperty =
+ AvaloniaProperty.Register(nameof(Source));
static ImageBrush()
{
@@ -30,7 +30,7 @@ namespace Avalonia.Media
/// Initializes a new instance of the class.
///
/// The image to draw.
- public ImageBrush(IBitmap source)
+ public ImageBrush(IBitmap? source)
{
Source = source;
}
@@ -38,7 +38,7 @@ namespace Avalonia.Media
///
/// Gets or sets the image to draw.
///
- public IBitmap Source
+ public IBitmap? Source
{
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
diff --git a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
index 7d9534c414..2564d89bac 100644
--- a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
+++ b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs
@@ -281,11 +281,12 @@ namespace Avalonia.Media
/// Pushes an opacity value.
///
/// The opacity.
+ /// The bounds.
/// A disposable used to undo the opacity.
- public PushedState PushOpacity(double opacity)
+ public PushedState PushOpacity(double opacity, Rect bounds)
//TODO: Eliminate platform-specific push opacity call
{
- PlatformImpl.PushOpacity(opacity);
+ PlatformImpl.PushOpacity(opacity, bounds);
return new PushedState(this, PushedState.PushedStateType.Opacity);
}
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
index f9892bf60c..668a907fdf 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
@@ -24,13 +24,13 @@ namespace Avalonia.Media.Immutable
/// The tile mode.
/// The bitmap interpolation mode.
public ImmutableImageBrush(
- IBitmap source,
+ IBitmap? source,
AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null,
double opacity = 1,
ImmutableTransform? transform = null,
- RelativePoint transformOrigin = new RelativePoint(),
+ RelativePoint transformOrigin = default,
RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform,
TileMode tileMode = TileMode.None,
@@ -61,6 +61,6 @@ namespace Avalonia.Media.Immutable
}
///
- public IBitmap Source { get; }
+ public IBitmap? Source { get; }
}
}
diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
index 0b625080e3..e9086eee37 100644
--- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
+++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Media.Immutable
/// The tile mode.
/// Controls the quality of interpolation.
public ImmutableVisualBrush(
- Visual visual,
+ Visual? visual,
AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null,
diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
index 8509067cd0..8962bc1586 100644
--- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs
@@ -128,7 +128,7 @@ namespace Avalonia.Platform
/// Pushes an opacity value.
///
/// The opacity.
- void PushOpacity(double opacity);
+ void PushOpacity(double opacity, Rect bounds);
///
/// Pops the latest pushed opacity value.
diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs
index 5826cfb2ff..d1964bf07e 100644
--- a/src/Avalonia.Base/Platform/IGeometryImpl.cs
+++ b/src/Avalonia.Base/Platform/IGeometryImpl.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using Avalonia.Media;
using Avalonia.Metadata;
@@ -47,7 +48,7 @@ namespace Avalonia.Platform
/// The stroke to use.
/// The point.
/// true if the geometry contains the point; otherwise, false.
- bool StrokeContains(IPen pen, Point point);
+ bool StrokeContains(IPen? pen, Point point);
///
/// Makes a clone of the geometry with the specified transform.
@@ -87,6 +88,7 @@ namespace Avalonia.Platform
/// If ture, the resulting snipped path will start with a BeginFigure call.
/// The resulting snipped path.
/// If the snipping operation is successful.
- bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry);
+ bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure,
+ [NotNullWhen(true)] out IGeometryImpl? segmentGeometry);
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
index b75d080cfd..6b380608fe 100644
--- a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
@@ -313,13 +313,13 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW
}
///
- public void PushOpacity(double opacity)
+ public void PushOpacity(double opacity, Rect bounds)
{
var next = NextDrawAs();
- if (next == null || !next.Item.Equals(opacity))
+ if (next == null || !next.Item.Equals(opacity, bounds))
{
- Add(new OpacityNode(opacity));
+ Add(new OpacityNode(opacity, bounds));
}
else
{
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
index 50df8bd32b..08e506536f 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
@@ -111,9 +111,9 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont
_impl.PopClip();
}
- public void PushOpacity(double opacity)
+ public void PushOpacity(double opacity, Rect bounds)
{
- _impl.PushOpacity(opacity);
+ _impl.PushOpacity(opacity, bounds);
}
public void PopOpacity()
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index 98be861afa..f9492d0015 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
@@ -41,9 +41,9 @@ namespace Avalonia.Rendering.Composition.Server
return;
Root!.RenderedVisuals++;
-
- if (Opacity != 1)
- canvas.PushOpacity(Opacity);
+
+ var boundsRect = new Rect(new Size(Size.X, Size.Y));
+
if (AdornedVisual != null)
{
canvas.PostTransform = Matrix.Identity;
@@ -54,15 +54,16 @@ namespace Avalonia.Rendering.Composition.Server
var transform = GlobalTransformMatrix;
canvas.PostTransform = MatrixUtils.ToMatrix(transform);
canvas.Transform = Matrix.Identity;
-
- var boundsRect = new Rect(new Size(Size.X, Size.Y));
+
+ if (Opacity != 1)
+ canvas.PushOpacity(Opacity, boundsRect);
if (ClipToBounds && !HandlesClipToBounds)
canvas.PushClip(Root!.SnapToDevicePixels(boundsRect));
if (Clip != null)
canvas.PushGeometryClip(Clip);
if(OpacityMaskBrush != null)
canvas.PushOpacityMask(OpacityMaskBrush, boundsRect);
-
+
RenderCore(canvas, currentTransformedClip);
// Hack to force invalidation of SKMatrix
diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
index 8e5dc38317..09d2d55ce3 100644
--- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
+++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs
@@ -117,7 +117,7 @@ namespace Avalonia.Rendering
}
using (context.PushPostTransform(m))
- using (context.PushOpacity(opacity))
+ using (context.PushOpacity(opacity, bounds))
using (clipToBounds
#pragma warning disable CS0618 // Type or member is obsolete
? visual is IVisualWithRoundRectClip roundClipVisual
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs
index e41e639067..f76a055934 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs
@@ -12,9 +12,11 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity push.
///
/// The opacity to push.
- public OpacityNode(double opacity)
+ /// The bounds.
+ public OpacityNode(double opacity, Rect bounds)
{
Opacity = opacity;
+ Bounds = bounds;
}
///
@@ -26,7 +28,7 @@ namespace Avalonia.Rendering.SceneGraph
}
///
- public Rect Bounds => default;
+ public Rect Bounds { get; }
///
/// Gets the opacity to be pushed or null if the operation represents a pop.
@@ -40,19 +42,20 @@ namespace Avalonia.Rendering.SceneGraph
/// Determines if this draw operation equals another.
///
/// The opacity of the other draw operation.
+ /// The bounds of the other draw operation.
/// True if the draw operations are the same, otherwise false.
///
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
///
- public bool Equals(double? opacity) => Opacity == opacity;
+ public bool Equals(double? opacity, Rect bounds) => Opacity == opacity && Bounds == bounds;
///
public void Render(IDrawingContextImpl context)
{
if (Opacity.HasValue)
{
- context.PushOpacity(Opacity.Value);
+ context.PushOpacity(Opacity.Value, Bounds);
}
else
{
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index 454f7eac9d..bc86558ab3 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -427,6 +427,7 @@ namespace Avalonia.Controls.Presenters
Viewport = finalSize;
Extent = Child!.Bounds.Size.Inflate(Child.Margin);
+ Offset = ScrollViewer.CoerceOffset(Extent, finalSize, Offset);
_isAnchorElementDirty = true;
return finalSize;
diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs
index f8ce5d23f6..6603e20a2a 100644
--- a/src/Avalonia.Controls/SelectableTextBlock.cs
+++ b/src/Avalonia.Controls/SelectableTextBlock.cs
@@ -336,7 +336,7 @@ namespace Avalonia.Controls
point = new Point(
MathUtilities.Clamp(point.X, 0, Math.Max(TextLayout.Bounds.Width, 0)),
- MathUtilities.Clamp(point.Y, 0, Math.Max(TextLayout.Bounds.Width, 0)));
+ MathUtilities.Clamp(point.Y, 0, Math.Max(TextLayout.Bounds.Height, 0)));
var hit = TextLayout.HitTestPoint(point);
var textPosition = hit.TextPosition;
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index ed11dec1d0..eea175eb3c 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -405,7 +405,31 @@ namespace Avalonia.Controls
{
return visual?.VisualRoot as TopLevel;
}
-
+
+ ///
+ /// Requests a to be inhibited.
+ /// The behavior remains inhibited until the return value is disposed.
+ /// The available set of s depends on the platform.
+ /// If a behavior is inhibited on a platform where this type is not supported the request will have no effect.
+ ///
+ public async Task RequestPlatformInhibition(PlatformInhibitionType type, string reason)
+ {
+ var platformBehaviorInhibition = PlatformImpl?.TryGetFeature();
+ if (platformBehaviorInhibition == null)
+ {
+ return Disposable.Create(() => { });
+ }
+
+ switch (type)
+ {
+ case PlatformInhibitionType.AppSleep:
+ await platformBehaviorInhibition.SetInhibitAppSleep(true, reason);
+ return Disposable.Create(() => platformBehaviorInhibition.SetInhibitAppSleep(false, reason).Wait());
+ default:
+ return Disposable.Create(() => { });
+ }
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -571,30 +595,6 @@ namespace Avalonia.Controls
/// The event args.
protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e);
- ///
- /// Requests a to be inhibited.
- /// The behavior remains inhibited until the return value is disposed.
- /// The available set of s depends on the platform.
- /// If a behavior is inhibited on a platform where this type is not supported the request will have no effect.
- ///
- protected async Task RequestPlatformInhibition(PlatformInhibitionType type, string reason)
- {
- var platformBehaviorInhibition = PlatformImpl?.TryGetFeature();
- if (platformBehaviorInhibition == null)
- {
- return Disposable.Create(() => { });
- }
-
- switch (type)
- {
- case PlatformInhibitionType.AppSleep:
- await platformBehaviorInhibition.SetInhibitAppSleep(true, reason);
- return Disposable.Create(() => platformBehaviorInhibition.SetInhibitAppSleep(false, reason).Wait());
- default:
- return Disposable.Create(() => { });
- }
- }
-
///
/// Tries to get a service from an , logging a
/// warning if not found.
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index 9f8e3e38c0..e9abfef673 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -190,7 +190,7 @@ namespace Avalonia.Controls
{
if (treeViewItem.ItemCount > 0 && !treeViewItem.IsExpanded)
{
- treeViewItem.IsExpanded = true;
+ treeViewItem.SetCurrentValue(IsExpandedProperty, true);
return true;
}
@@ -201,7 +201,7 @@ namespace Avalonia.Controls
{
if (treeViewItem.ItemCount > 0 && treeViewItem.IsExpanded)
{
- treeViewItem.IsExpanded = false;
+ treeViewItem.SetCurrentValue(IsExpandedProperty, false);
return true;
}
@@ -214,7 +214,7 @@ namespace Avalonia.Controls
{
if (treeViewItem.IsFocused)
{
- treeViewItem.IsExpanded = false;
+ treeViewItem.SetCurrentValue(IsExpandedProperty, false);
}
else
{
@@ -265,7 +265,7 @@ namespace Avalonia.Controls
{
if (ItemCount > 0)
{
- IsExpanded = !IsExpanded;
+ SetCurrentValue(IsExpandedProperty, !IsExpanded);
e.Handled = true;
}
}
diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs
index 634efbd699..8e7690aa6c 100644
--- a/src/Avalonia.Controls/VirtualizingStackPanel.cs
+++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs
@@ -403,7 +403,7 @@ namespace Avalonia.Controls
if (firstIndex == -1)
{
estimatedElementSize = EstimateElementSizeU();
- firstIndex = (int)(viewportStart / estimatedElementSize);
+ firstIndex = Math.Min((int)(viewportStart / estimatedElementSize), maxIndex);
firstIndexU = firstIndex * estimatedElementSize;
}
@@ -411,13 +411,13 @@ namespace Avalonia.Controls
{
if (estimatedElementSize == -1)
estimatedElementSize = EstimateElementSizeU();
- lastIndex = (int)(viewportEnd / estimatedElementSize);
+ lastIndex = Math.Min((int)(viewportEnd / estimatedElementSize), maxIndex);
}
return new MeasureViewport
{
- firstIndex = MathUtilities.Clamp(firstIndex, 0, maxIndex),
- lastIndex = MathUtilities.Clamp(lastIndex, 0, maxIndex),
+ firstIndex = firstIndex,
+ lastIndex = lastIndex,
viewportUStart = viewportStart,
viewportUEnd = viewportEnd,
startU = firstIndexU,
@@ -1131,6 +1131,7 @@ namespace Avalonia.Controls
// The removed range was before the realized elements. Update the first index and
// the indexes of the realized elements.
_firstIndex -= count;
+ _startUUnstable = true;
var newIndex = _firstIndex;
for (var i = 0; i < _elements.Count; ++i)
diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
index 3b1c6cc7b1..51af5862cf 100644
--- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
+++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
@@ -5,16 +5,40 @@
enable
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.FreeDesktop/DBusCallQueue.cs b/src/Avalonia.FreeDesktop/DBusCallQueue.cs
index e7c07dcbf9..b853626d45 100644
--- a/src/Avalonia.FreeDesktop/DBusCallQueue.cs
+++ b/src/Avalonia.FreeDesktop/DBusCallQueue.cs
@@ -7,19 +7,20 @@ namespace Avalonia.FreeDesktop
class DBusCallQueue
{
private readonly Func _errorHandler;
+ private readonly Queue- _q = new();
- record Item(Func Callback)
+ private bool _processing;
+
+ private record Item(Func Callback)
{
public Action? OnFinish;
}
- private Queue
- _q = new Queue
- ();
- private bool _processing;
public DBusCallQueue(Func errorHandler)
{
_errorHandler = errorHandler;
}
-
+
public void Enqueue(Func cb)
{
_q.Enqueue(new Item(cb));
@@ -42,7 +43,7 @@ namespace Avalonia.FreeDesktop
Process();
return tcs.Task;
}
-
+
public Task EnqueueAsync(Func> cb)
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -62,7 +63,7 @@ namespace Avalonia.FreeDesktop
return tcs.Task;
}
- async void Process()
+ private async void Process()
{
if(_processing)
return;
diff --git a/src/Avalonia.FreeDesktop/DBusFileChooser.cs b/src/Avalonia.FreeDesktop/DBusFileChooser.cs
deleted file mode 100644
index 24db614a02..0000000000
--- a/src/Avalonia.FreeDesktop/DBusFileChooser.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-using Tmds.DBus;
-
-[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)]
-namespace Avalonia.FreeDesktop
-{
- [DBusInterface("org.freedesktop.portal.FileChooser")]
- internal interface IFileChooser : IDBusObject
- {
- Task OpenFileAsync(string ParentWindow, string Title, IDictionary Options);
- Task SaveFileAsync(string ParentWindow, string Title, IDictionary Options);
- Task SaveFilesAsync(string ParentWindow, string Title, IDictionary Options);
- Task GetAsync(string prop);
- Task GetAllAsync();
- Task SetAsync(string prop, object val);
- Task WatchPropertiesAsync(Action handler);
- }
-
- [Dictionary]
- internal class FileChooserProperties
- {
- public uint Version { get; set; }
- }
-
- internal static class FileChooserExtensions
- {
- public static Task GetVersionAsync(this IFileChooser o) => o.GetAsync("version");
- }
-}
diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs
index fac77521dc..da74f15a3e 100644
--- a/src/Avalonia.FreeDesktop/DBusHelper.cs
+++ b/src/Avalonia.FreeDesktop/DBusHelper.cs
@@ -1,51 +1,12 @@
using System;
using System.Threading;
using Avalonia.Logging;
-using Avalonia.Threading;
-using Tmds.DBus;
+using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop
{
internal static class DBusHelper
{
- ///
- /// This class uses synchronous execution at DBus connection establishment stage
- /// then switches to using AvaloniaSynchronizationContext
- ///
- private class DBusSyncContext : SynchronizationContext
- {
- private readonly object _lock = new();
- private SynchronizationContext? _ctx;
-
- public override void Post(SendOrPostCallback d, object? state)
- {
- lock (_lock)
- {
- if (_ctx is not null)
- _ctx?.Post(d, state);
- else
- d(state);
- }
- }
-
- public override void Send(SendOrPostCallback d, object? state)
- {
- lock (_lock)
- {
- if (_ctx is not null)
- _ctx?.Send(d, state);
- else
- d(state);
- }
- }
-
- public void Initialized()
- {
- lock (_lock)
- _ctx = new AvaloniaSynchronizationContext();
- }
- }
-
public static Connection? Connection { get; private set; }
public static Connection? TryInitialize(string? dbusAddress = null)
@@ -56,19 +17,14 @@ namespace Avalonia.FreeDesktop
var oldContext = SynchronizationContext.Current;
try
{
-
- var dbusContext = new DBusSyncContext();
- SynchronizationContext.SetSynchronizationContext(dbusContext);
- var conn = new Connection(new ClientConnectionOptions(dbusAddress ?? Address.Session)
+ var conn = new Connection(new ClientConnectionOptions(dbusAddress ?? Address.Session!)
{
- AutoConnect = false,
- SynchronizationContext = dbusContext
+ AutoConnect = false
});
+
// Connect synchronously
- conn.ConnectAsync().Wait();
+ conn.ConnectAsync().GetAwaiter().GetResult();
- // Initialize a brand new sync-context
- dbusContext.Initialized();
Connection = conn;
}
catch (Exception e)
diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs
index 7d97c7cd36..d3c14f285d 100644
--- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs
+++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs
@@ -5,7 +5,8 @@ using System.Threading.Tasks;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Logging;
-using Tmds.DBus;
+using Tmds.DBus.Protocol;
+using Tmds.DBus.SourceGenerator;
namespace Avalonia.FreeDesktop.DBusIme
{
@@ -46,20 +47,25 @@ namespace Avalonia.FreeDesktop.DBusIme
public DBusTextInputMethodBase(Connection connection, params string[] knownNames)
{
- _queue = new DBusCallQueue(QueueOnError);
+ _queue = new DBusCallQueue(QueueOnErrorAsync);
Connection = connection;
_knownNames = knownNames;
- Watch();
+ _ = WatchAsync();
}
public ITextInputMethodClient Client => _client;
- public bool IsActive => _client != null;
+ public bool IsActive => _client is not null;
- async void Watch()
+ private async Task WatchAsync()
{
foreach (var name in _knownNames)
- _disposables.Add(await Connection.ResolveServiceOwnerAsync(name, OnNameChange));
+ {
+ var dbus = new OrgFreedesktopDBus(Connection, "org.freedesktop.DBus", "/org/freedesktop/DBus");
+ _disposables.Add(await dbus.WatchNameOwnerChangedAsync(OnNameChange));
+ var nameOwner = await dbus.GetNameOwnerAsync(name);
+ OnNameChange(null, (name, null, nameOwner));
+ }
}
protected abstract Task Connect(string name);
@@ -67,9 +73,12 @@ namespace Avalonia.FreeDesktop.DBusIme
protected string GetAppName() =>
Application.Current?.Name ?? Assembly.GetEntryAssembly()?.GetName()?.Name ?? "Avalonia";
- private async void OnNameChange(ServiceOwnerChangedEventArgs args)
+ private async void OnNameChange(Exception? e, (string ServiceName, string? OldOwner, string? NewOwner) args)
{
- if (args.NewOwner != null && _currentName == null)
+ if (e is not null)
+ return;
+
+ if (args.NewOwner is not null && _currentName is null)
{
_onlineNamesQueue.Enqueue(args.ServiceName);
if (!_connecting)
@@ -89,10 +98,10 @@ namespace Avalonia.FreeDesktop.DBusIme
return;
}
}
- catch (Exception e)
+ catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, "IME")
- ?.Log(this, "Unable to create IME input context:\n" + e);
+ ?.Log(this, "Unable to create IME input context:\n" + ex);
}
}
}
@@ -105,7 +114,7 @@ namespace Avalonia.FreeDesktop.DBusIme
}
// IME has crashed
- if (args.NewOwner == null && args.ServiceName == _currentName)
+ if (args.NewOwner is null && args.ServiceName == _currentName)
{
_currentName = null;
foreach (var s in _disposables)
@@ -116,11 +125,11 @@ namespace Avalonia.FreeDesktop.DBusIme
Reset();
// Watch again
- Watch();
+ _ = WatchAsync();
}
}
- protected virtual Task Disconnect()
+ protected virtual Task DisconnectAsync()
{
return Task.CompletedTask;
}
@@ -136,13 +145,13 @@ namespace Avalonia.FreeDesktop.DBusIme
_imeActive = null;
}
- async Task QueueOnError(Exception e)
+ private async Task QueueOnErrorAsync(Exception e)
{
Logger.TryGet(LogEventLevel.Error, "IME")
?.Log(this, "Error:\n" + e);
try
{
- await Disconnect();
+ await DisconnectAsync();
}
catch (Exception ex)
{
@@ -157,23 +166,16 @@ namespace Avalonia.FreeDesktop.DBusIme
protected void AddDisposable(IDisposable? d)
{
- if(d is { })
+ if (d is { })
_disposables.Add(d);
}
-
+
public void Dispose()
{
foreach(var d in _disposables)
d.Dispose();
_disposables.Clear();
- try
- {
- Disconnect().ContinueWith(_ => { });
- }
- catch
- {
- // fire and forget
- }
+ _ = DisconnectAsync();
_currentName = null;
}
@@ -182,13 +184,13 @@ namespace Avalonia.FreeDesktop.DBusIme
protected abstract Task ResetContextCore();
protected abstract Task HandleKeyCore(RawKeyEventArgs args, int keyVal, int keyCode);
- void UpdateActive()
+ private void UpdateActive()
{
_queue.Enqueue(async () =>
{
if(!IsConnected)
return;
-
+
var active = _windowActive && IsActive;
if (active != _imeActive)
{
@@ -204,7 +206,7 @@ namespace Avalonia.FreeDesktop.DBusIme
_windowActive = active;
UpdateActive();
}
-
+
void ITextInputMethodImpl.SetClient(ITextInputMethodClient? client)
{
_client = client;
@@ -227,7 +229,7 @@ namespace Avalonia.FreeDesktop.DBusIme
// Error, disconnect
catch (Exception e)
{
- await QueueOnError(e);
+ await QueueOnErrorAsync(e);
return false;
}
}
@@ -240,7 +242,7 @@ namespace Avalonia.FreeDesktop.DBusIme
}
protected void FireCommit(string s) => _onCommit?.Invoke(s);
-
+
private Action? _onForward;
event Action IX11InputMethodControl.ForwardKey
{
@@ -249,8 +251,8 @@ namespace Avalonia.FreeDesktop.DBusIme
}
protected void FireForward(X11InputMethodForwardedKey k) => _onForward?.Invoke(k);
-
- void UpdateCursorRect()
+
+ private void UpdateCursorRect()
{
_queue.Enqueue(async () =>
{
@@ -265,7 +267,7 @@ namespace Avalonia.FreeDesktop.DBusIme
}
});
}
-
+
void IX11InputMethodControl.UpdateWindowInfo(PixelPoint position, double scaling)
{
_windowPosition = position;
diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxDBus.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxDBus.cs
deleted file mode 100644
index 06afacaa29..0000000000
--- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxDBus.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-using Tmds.DBus;
-
-[assembly: InternalsVisibleTo(Tmds.DBus.Connection.DynamicAssemblyName)]
-namespace Avalonia.FreeDesktop.DBusIme.Fcitx
-{
- [DBusInterface("org.fcitx.Fcitx.InputMethod")]
- interface IFcitxInputMethod : IDBusObject
- {
- Task<(int icid, bool enable, uint keyval1, uint state1, uint keyval2, uint state2)> CreateICv3Async(
- string Appname, int Pid);
- }
-
-
- [DBusInterface("org.fcitx.Fcitx.InputContext")]
- interface IFcitxInputContext : IDBusObject
- {
- Task EnableICAsync();
- Task CloseICAsync();
- Task FocusInAsync();
- Task FocusOutAsync();
- Task ResetAsync();
- Task MouseEventAsync(int X);
- Task SetCursorLocationAsync(int X, int Y);
- Task SetCursorRectAsync(int X, int Y, int W, int H);
- Task SetCapacityAsync(uint Caps);
- Task SetSurroundingTextAsync(string Text, uint Cursor, uint Anchor);
- Task SetSurroundingTextPositionAsync(uint Cursor, uint Anchor);
- Task DestroyICAsync();
- Task ProcessKeyEventAsync(uint Keyval, uint Keycode, uint State, int Type, uint Time);
- Task WatchEnableIMAsync(Action handler, Action? onError = null);
- Task WatchCloseIMAsync(Action handler, Action? onError = null);
- Task WatchCommitStringAsync(Action handler, Action? onError = null);
- Task WatchCurrentIMAsync(Action<(string name, string uniqueName, string langCode)> handler, Action? onError = null);
- Task WatchUpdatePreeditAsync(Action<(string str, int cursorpos)> handler, Action? onError = null);
- Task WatchUpdateFormattedPreeditAsync(Action<((string, int)[] str, int cursorpos)> handler, Action? onError = null);
- Task WatchUpdateClientSideUIAsync(Action<(string auxup, string auxdown, string preedit, string candidateword, string imname, int cursorpos)> handler, Action? onError = null);
- Task WatchForwardKeyAsync(Action<(uint keyval, uint state, int type)> handler, Action? onError = null);
- Task WatchDeleteSurroundingTextAsync(Action<(int offset, uint nchar)> handler, Action? onError = null);
- }
-
- [DBusInterface("org.fcitx.Fcitx.InputContext1")]
- interface IFcitxInputContext1 : IDBusObject
- {
- Task FocusInAsync();
- Task FocusOutAsync();
- Task ResetAsync();
- Task SetCursorRectAsync(int X, int Y, int W, int H);
- Task SetCapabilityAsync(ulong Caps);
- Task SetSurroundingTextAsync(string Text, uint Cursor, uint Anchor);
- Task SetSurroundingTextPositionAsync(uint Cursor, uint Anchor);
- Task DestroyICAsync();
- Task ProcessKeyEventAsync(uint Keyval, uint Keycode, uint State, bool Type, uint Time);
- Task WatchCommitStringAsync(Action handler, Action? onError = null);
- Task WatchCurrentIMAsync(Action<(string name, string uniqueName, string langCode)> handler, Action? onError = null);
- Task WatchUpdateFormattedPreeditAsync(Action<((string, int)[] str, int cursorpos)> handler, Action? onError = null);
- Task WatchForwardKeyAsync(Action<(uint keyval, uint state, bool type)> handler, Action? onError = null);
- Task WatchDeleteSurroundingTextAsync(Action<(int offset, uint nchar)> handler, Action? onError = null);
- }
-
- [DBusInterface("org.fcitx.Fcitx.InputMethod1")]
- interface IFcitxInputMethod1 : IDBusObject
- {
- Task<(ObjectPath path, byte[] data)> CreateInputContextAsync((string, string)[] arg0);
- }
-}
diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxEnums.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxEnums.cs
index 6510a5877a..fafc66b6b9 100644
--- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxEnums.cs
+++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxEnums.cs
@@ -2,45 +2,45 @@ using System;
namespace Avalonia.FreeDesktop.DBusIme.Fcitx
{
- enum FcitxKeyEventType
+ internal enum FcitxKeyEventType
{
FCITX_PRESS_KEY,
FCITX_RELEASE_KEY
- };
-
+ }
+
[Flags]
- enum FcitxCapabilityFlags
+ internal enum FcitxCapabilityFlags
{
CAPACITY_NONE = 0,
- CAPACITY_CLIENT_SIDE_UI = (1 << 0),
- CAPACITY_PREEDIT = (1 << 1),
- CAPACITY_CLIENT_SIDE_CONTROL_STATE = (1 << 2),
- CAPACITY_PASSWORD = (1 << 3),
- CAPACITY_FORMATTED_PREEDIT = (1 << 4),
- CAPACITY_CLIENT_UNFOCUS_COMMIT = (1 << 5),
- CAPACITY_SURROUNDING_TEXT = (1 << 6),
- CAPACITY_EMAIL = (1 << 7),
- CAPACITY_DIGIT = (1 << 8),
- CAPACITY_UPPERCASE = (1 << 9),
- CAPACITY_LOWERCASE = (1 << 10),
- CAPACITY_NOAUTOUPPERCASE = (1 << 11),
- CAPACITY_URL = (1 << 12),
- CAPACITY_DIALABLE = (1 << 13),
- CAPACITY_NUMBER = (1 << 14),
- CAPACITY_NO_ON_SCREEN_KEYBOARD = (1 << 15),
- CAPACITY_SPELLCHECK = (1 << 16),
- CAPACITY_NO_SPELLCHECK = (1 << 17),
- CAPACITY_WORD_COMPLETION = (1 << 18),
- CAPACITY_UPPERCASE_WORDS = (1 << 19),
- CAPACITY_UPPERCASE_SENTENCES = (1 << 20),
- CAPACITY_ALPHA = (1 << 21),
- CAPACITY_NAME = (1 << 22),
- CAPACITY_GET_IM_INFO_ON_FOCUS = (1 << 23),
- CAPACITY_RELATIVE_CURSOR_RECT = (1 << 24),
- };
+ CAPACITY_CLIENT_SIDE_UI = 1 << 0,
+ CAPACITY_PREEDIT = 1 << 1,
+ CAPACITY_CLIENT_SIDE_CONTROL_STATE = 1 << 2,
+ CAPACITY_PASSWORD = 1 << 3,
+ CAPACITY_FORMATTED_PREEDIT = 1 << 4,
+ CAPACITY_CLIENT_UNFOCUS_COMMIT = 1 << 5,
+ CAPACITY_SURROUNDING_TEXT = 1 << 6,
+ CAPACITY_EMAIL = 1 << 7,
+ CAPACITY_DIGIT = 1 << 8,
+ CAPACITY_UPPERCASE = 1 << 9,
+ CAPACITY_LOWERCASE = 1 << 10,
+ CAPACITY_NOAUTOUPPERCASE = 1 << 11,
+ CAPACITY_URL = 1 << 12,
+ CAPACITY_DIALABLE = 1 << 13,
+ CAPACITY_NUMBER = 1 << 14,
+ CAPACITY_NO_ON_SCREEN_KEYBOARD = 1 << 15,
+ CAPACITY_SPELLCHECK = 1 << 16,
+ CAPACITY_NO_SPELLCHECK = 1 << 17,
+ CAPACITY_WORD_COMPLETION = 1 << 18,
+ CAPACITY_UPPERCASE_WORDS = 1 << 19,
+ CAPACITY_UPPERCASE_SENTENCES = 1 << 20,
+ CAPACITY_ALPHA = 1 << 21,
+ CAPACITY_NAME = 1 << 22,
+ CAPACITY_GET_IM_INFO_ON_FOCUS = 1 << 23,
+ CAPACITY_RELATIVE_CURSOR_RECT = 1 << 24
+ }
[Flags]
- enum FcitxKeyState
+ internal enum FcitxKeyState
{
FcitxKeyState_None = 0,
FcitxKeyState_Shift = 1 << 0,
@@ -63,5 +63,5 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
FcitxKeyState_Hyper = 1 << 27,
FcitxKeyState_Meta = 1 << 28,
FcitxKeyState_UsedMask = 0x5c001fff
- };
+ }
}
diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs
index 6c503edb41..00d05e59a3 100644
--- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs
+++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs
@@ -1,19 +1,20 @@
using System;
using System.Threading.Tasks;
+using Tmds.DBus.SourceGenerator;
namespace Avalonia.FreeDesktop.DBusIme.Fcitx
{
internal class FcitxICWrapper
{
- private readonly IFcitxInputContext1? _modern;
- private readonly IFcitxInputContext? _old;
+ private readonly OrgFcitxFcitxInputContext1? _modern;
+ private readonly OrgFcitxFcitxInputContext? _old;
- public FcitxICWrapper(IFcitxInputContext old)
+ public FcitxICWrapper(OrgFcitxFcitxInputContext old)
{
_old = old;
}
- public FcitxICWrapper(IFcitxInputContext1 modern)
+ public FcitxICWrapper(OrgFcitxFcitxInputContext1 modern)
{
_modern = modern;
}
@@ -21,32 +22,30 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
public Task FocusInAsync() => _old?.FocusInAsync() ?? _modern?.FocusInAsync() ?? Task.CompletedTask;
public Task FocusOutAsync() => _old?.FocusOutAsync() ?? _modern?.FocusOutAsync() ?? Task.CompletedTask;
-
+
public Task ResetAsync() => _old?.ResetAsync() ?? _modern?.ResetAsync() ?? Task.CompletedTask;
public Task SetCursorRectAsync(int x, int y, int w, int h) =>
_old?.SetCursorRectAsync(x, y, w, h) ?? _modern?.SetCursorRectAsync(x, y, w, h) ?? Task.CompletedTask;
+
public Task DestroyICAsync() => _old?.DestroyICAsync() ?? _modern?.DestroyICAsync() ?? Task.CompletedTask;
public async Task ProcessKeyEventAsync(uint keyVal, uint keyCode, uint state, int type, uint time)
{
- if(_old!=null)
+ if (_old is not null)
return await _old.ProcessKeyEventAsync(keyVal, keyCode, state, type, time) != 0;
return await (_modern?.ProcessKeyEventAsync(keyVal, keyCode, state, type > 0, time) ?? Task.FromResult(false));
}
- public Task WatchCommitStringAsync(Action handler) =>
- _old?.WatchCommitStringAsync(handler)
- ?? _modern?.WatchCommitStringAsync(handler)
- ?? Task.FromResult(default(IDisposable?));
+ public ValueTask WatchCommitStringAsync(Action handler) =>
+ _old?.WatchCommitStringAsync(handler)
+ ?? _modern?.WatchCommitStringAsync(handler)
+ ?? new ValueTask(default(IDisposable?));
- public Task WatchForwardKeyAsync(Action<(uint keyval, uint state, int type)> handler)
- {
- return _old?.WatchForwardKeyAsync(handler)
- ?? _modern?.WatchForwardKeyAsync(ev =>
- handler((ev.keyval, ev.state, ev.type ? 1 : 0)))
- ?? Task.FromResult(default(IDisposable?));
- }
+ public ValueTask WatchForwardKeyAsync(Action handler) =>
+ _old?.WatchForwardKeyAsync(handler)
+ ?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.keyval, ev.state, ev.type ? 1 : 0)))
+ ?? new ValueTask(default(IDisposable?));
public Task SetCapacityAsync(uint flags) =>
_old?.SetCapacityAsync(flags) ?? _modern?.SetCapabilityAsync(flags) ?? Task.CompletedTask;
diff --git a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs
index 0f499c6066..1cf3507cc2 100644
--- a/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs
+++ b/src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs
@@ -1,11 +1,11 @@
using System;
using System.Diagnostics;
-using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
-using Tmds.DBus;
+using Tmds.DBus.Protocol;
+using Tmds.DBus.SourceGenerator;
namespace Avalonia.FreeDesktop.DBusIme.Fcitx
{
@@ -14,32 +14,24 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
private FcitxICWrapper? _context;
private FcitxCapabilityFlags? _lastReportedFlags;
- public FcitxX11TextInputMethod(Connection connection) : base(connection,
- "org.fcitx.Fcitx",
- "org.freedesktop.portal.Fcitx"
- )
- {
-
- }
+ public FcitxX11TextInputMethod(Connection connection) : base(connection, "org.fcitx.Fcitx", "org.freedesktop.portal.Fcitx") { }
protected override async Task Connect(string name)
{
if (name == "org.fcitx.Fcitx")
{
- var method = Connection.CreateProxy(name, "/inputmethod");
+ var method = new OrgFcitxFcitxInputMethod(Connection, name, "/inputmethod");
var resp = await method.CreateICv3Async(GetAppName(),
Process.GetCurrentProcess().Id);
- var proxy = Connection.CreateProxy(name,
- "/inputcontext_" + resp.icid);
-
+ var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.icid}");
_context = new FcitxICWrapper(proxy);
}
else
{
- var method = Connection.CreateProxy(name, "/inputmethod");
+ var method = new OrgFcitxFcitxInputMethod1(Connection, name, "/inputmethod");
var resp = await method.CreateInputContextAsync(new[] { ("appName", GetAppName()) });
- var proxy = Connection.CreateProxy(name, resp.path);
+ var proxy = new OrgFcitxFcitxInputContext1(Connection, name, resp.Item1);
_context = new FcitxICWrapper(proxy);
}
@@ -48,7 +40,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
return true;
}
- protected override Task Disconnect() => _context?.DestroyICAsync() ?? Task.CompletedTask;
+ protected override Task DisconnectAsync() => _context?.DestroyICAsync() ?? Task.CompletedTask;
protected override void OnDisconnected() => _context = null;
@@ -63,14 +55,12 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
Math.Max(1, cursorRect.Height))
?? Task.CompletedTask;
- protected override Task SetActiveCore(bool active)=> (active
+ protected override Task SetActiveCore(bool active)=> (active
? _context?.FocusInAsync()
: _context?.FocusOutAsync())
?? Task.CompletedTask;
-
- protected override Task ResetContextCore() => _context?.ResetAsync()
- ?? Task.CompletedTask;
+ protected override Task ResetContextCore() => _context?.ResetAsync() ?? Task.CompletedTask;
protected override async Task HandleKeyCore(RawKeyEventArgs args, int keyVal, int keyCode)
{
@@ -87,17 +77,13 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
var type = args.Type == RawKeyEventType.KeyDown ?
FcitxKeyEventType.FCITX_PRESS_KEY :
FcitxKeyEventType.FCITX_RELEASE_KEY;
- if (_context is { })
- {
+ if (_context is not null)
return await _context.ProcessKeyEventAsync((uint)keyVal, (uint)keyCode, (uint)state, (int)type,
(uint)args.Timestamp).ConfigureAwait(false);
- }
- else
- {
- return false;
- }
+
+ return false;
}
-
+
public override void SetOptions(TextInputOptions options) =>
Enqueue(async () =>
{
@@ -127,7 +113,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
}
});
- private void OnForward((uint keyval, uint state, int type) ev)
+ private void OnForward(Exception? e, (uint keyval, uint state, int type) ev)
{
var state = (FcitxKeyState)ev.state;
KeyModifiers mods = default;
@@ -149,6 +135,12 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
});
}
- private void OnCommitString(string s) => FireCommit(s);
+ private void OnCommitString(Exception? e, string s)
+ {
+ if (e is not null)
+ return;
+
+ FireCommit(s);
+ }
}
}
diff --git a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusDBus.cs b/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusDBus.cs
deleted file mode 100644
index 4ef034adb9..0000000000
--- a/src/Avalonia.FreeDesktop/DBusIme/IBus/IBusDBus.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using Tmds.DBus;
-
-[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)]
-namespace Avalonia.FreeDesktop.DBusIme.IBus
-{
- [DBusInterface("org.freedesktop.IBus.InputContext")]
- interface IIBusInputContext : IDBusObject
- {
- Task ProcessKeyEventAsync(uint Keyval, uint Keycode, uint State);
- Task SetCursorLocationAsync(int X, int Y, int W, int H);
- Task FocusInAsync();
- Task FocusOutAsync();
- Task ResetAsync();
- Task SetCapabilitiesAsync(uint Caps);
- Task PropertyActivateAsync(string Name, int State);
- Task SetEngineAsync(string Name);
- Task