Without submodule
MODIFY
.gitmodules
.gitmodules
-[submodule "Zafiro.Avalonia"]
- path = Zafiro.Avalonia
- url = https://github.com/SuperJMN-Zafiro/Zafiro.Avalonia.git

MODIFY
WalletWasabi.Fluent.Desktop/packages.lock.json
WalletWasabi.Fluent.Desktop/packages.lock.json
},
"Avalonia.Xaml.Behaviors": {
"type": "Transitive",
- "resolved": "0.10.13.1",
- "contentHash": "Z+wcwoJCKBBds6aHQ7Fddph3027h9tXswlrOxVh7dJEqTUK0MNYHUlyjOaqIOGl2ng3uBK6cs2L2wXG5Cwzf5g==",
+ "resolved": "0.10.15",
+ "contentHash": "jbxlCjdIa49tSYsYfo54NJcngzhSXpvhs+pWymQjqfKcWBS6yT4CLK6U/nyquYT1XlHdyXJCi/M/R2Xn+rtI7Q==",
"dependencies": {
- "Avalonia": "0.10.13",
- "Avalonia.Xaml.Interactions": "0.10.13.1",
- "Avalonia.Xaml.Interactivity": "0.10.13.1"
+ "Avalonia": "0.10.15",
+ "Avalonia.Xaml.Interactions": "0.10.15",
+ "Avalonia.Xaml.Interactivity": "0.10.15"
}
},
"Avalonia.Xaml.Interactions": {
"type": "Transitive",
- "resolved": "0.10.13.1",
- "contentHash": "FfBzAwH9TXVk3P+diK8V0t/dYufXCm1tpY7Ok14bll3xMlI+vICObxE7yzXriFTfQO7oaFONaPgdspQnfGKwEg==",
+ "resolved": "0.10.15",
+ "contentHash": "7580+UOzU8vVVJ1qH2BS+lKtJLToO1cezy29FhDGuFwVuPN8M3aYkq6LlAD5NBo2xkeaOHm7/JqTTyWbVJLOKA==",
"dependencies": {
- "Avalonia": "0.10.13",
- "Avalonia.Xaml.Interactivity": "0.10.13.1"
+ "Avalonia": "0.10.15",
+ "Avalonia.Xaml.Interactivity": "0.10.15"
}
},
"Avalonia.Xaml.Interactivity": {
"type": "Transitive",
- "resolved": "0.10.14",
- "contentHash": "/AtmTGM+QAjY+8RCkeAbRP5QcEfoWKSDsJYNGnoikRR0xRXlbONvkzhnR0ukxjWkf0NCUh6Sn2dy/HkqWP4y7Q==",
+ "resolved": "0.10.15",
+ "contentHash": "FO6Ob/fsSK02chQNB79BouAjB9o7qBg1cCcQDjhCQzRc04yE998+cpfMFpkL9ODbnUW1YcDTSKDTW0ZjTX4asQ==",
"dependencies": {
- "Avalonia": "0.10.14"
+ "Avalonia": "0.10.15"
}
},
"DynamicData": {
"Avalonia.Diagnostics": "0.10.999-cibuild0021004-beta",
"Avalonia.ReactiveUI": "0.10.999-cibuild0021004-beta",
"Avalonia.Skia": "0.10.999-cibuild0021004-beta",
- "Avalonia.Xaml.Behaviors": "0.10.13.1",
+ "Avalonia.Xaml.Behaviors": "0.10.15",
"System.Drawing.Common": "6.0.0",
"System.Reactive": "5.0.0",
"System.Runtime": "4.3.1",
- "WalletWasabi": "1.0.0",
- "Zafiro.Avalonia": "1.0.0"
- }
- },
- "zafiro.avalonia": {
- "type": "Project",
- "dependencies": {
- "Avalonia": "0.10.15",
- "Avalonia.Xaml.Interactivity": "0.10.14"
+ "WalletWasabi": "1.0.0"
}
}
},

ADD
WalletWasabi.Fluent/Behaviors/ShowAttachedFlyoutWhenFocusedBehavior.cs
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Diagnostics;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.VisualTree;
+using Avalonia.Xaml.Interactivity;
+using WalletWasabi.Fluent.Extensions;
+using WalletWasabi.Fluent.Helpers;
+
+namespace WalletWasabi.Fluent.Behaviors;
+
+public class ShowAttachedFlyoutWhenFocusedBehavior : Behavior<Control>
+{
+ public static readonly StyledProperty<bool> IsFlyoutOpenProperty =
+ AvaloniaProperty.Register<ShowAttachedFlyoutWhenFocusedBehavior, bool>(
+ nameof(IsFlyoutOpen));
+
+ private readonly CompositeDisposable _disposables = new();
+
+ private FlyoutController? _flyoutController;
+
+ public bool IsFlyoutOpen
+ {
+ get => GetValue(IsFlyoutOpenProperty);
+ set => SetValue(IsFlyoutOpenProperty, value);
+ }
+
+ protected override void OnAttachedToVisualTree()
+ {
+ base.OnAttachedToVisualTree();
+
+ if (AssociatedObject is null)
+ {
+ return;
+ }
+
+ var flyoutBase = FlyoutBase.GetAttachedFlyout(AssociatedObject);
+ if (flyoutBase is null)
+ {
+ return;
+ }
+
+ if (AssociatedObject.GetVisualRoot() is not Control visualRoot)
+ {
+ return;
+ }
+
+ _flyoutController = new FlyoutController(flyoutBase, AssociatedObject)
+ .DisposeWith(_disposables);
+
+ DescendantPressed(visualRoot)
+ .Select(descendant => AssociatedObject.IsVisualAncestorOf(descendant))
+ .Do(isAncestor =>
+ {
+ _flyoutController.IsOpen = isAncestor;
+ IsFlyoutOpen = isAncestor;
+ })
+ .Subscribe()
+ .DisposeWith(_disposables);
+
+ Observable.FromEventPattern(AssociatedObject, nameof(InputElement.GotFocus))
+ .Do(_ =>
+ {
+ _flyoutController.IsOpen = true;
+ IsFlyoutOpen = true;
+ })
+ .Subscribe()
+ .DisposeWith(_disposables);
+
+ Observable.FromEventPattern(AssociatedObject, nameof(InputElement.LostFocus))
+ .Where(_ => !IsFocusInside(flyoutBase))
+ .Do(_ =>
+ {
+ _flyoutController.IsOpen = false;
+ IsFlyoutOpen = false;
+ })
+ .Subscribe()
+ .DisposeWith(_disposables);
+
+ this.GetObservable(IsFlyoutOpenProperty).Subscribe(b => _flyoutController.IsOpen = b);
+
+ if (IsFlyoutOpen)
+ {
+ _flyoutController.IsOpen = false;
+ }
+ }
+
+ protected override void OnDetachedFromVisualTree()
+ {
+ _disposables.Dispose();
+ }
+
+ private static IObservable<Visual> DescendantPressed(IInteractive interactive)
+ {
+ return
+ from eventPattern in interactive.OnEvent(InputElement.PointerPressedEvent, RoutingStrategies.Tunnel)
+ let source = eventPattern.EventArgs.Source as Visual
+ where source is not null
+ where source is not LightDismissOverlayLayer
+ select source;
+ }
+
+ private static bool IsFocusInside(IPopupHostProvider popupHostProvider)
+ {
+ var focusManager = FocusManager.Instance;
+
+ if (focusManager?.Current is null)
+ {
+ return false;
+ }
+
+ var popupPresenter = popupHostProvider.PopupHost?.Presenter;
+
+ if (popupPresenter is null)
+ {
+ return false;
+ }
+
+ var currentlyFocused = focusManager.Current;
+ return popupPresenter.IsVisualAncestorOf(currentlyFocused);
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Extensions/AvaloniaMixin.cs
+using System.Reactive;
+using System.Reactive.Linq;
+using Avalonia.Interactivity;
+
+namespace WalletWasabi.Fluent.Extensions;
+
+public static class AvaloniaMixin
+{
+ public static IObservable<EventPattern<TEventArgs>> OnEvent<TEventArgs>(this IInteractive target, RoutedEvent<TEventArgs> routedEvent, RoutingStrategies routingStrategies) where TEventArgs : RoutedEventArgs
+ {
+ return Observable.FromEventPattern<TEventArgs>(
+ add => target.AddHandler(routedEvent, add, routingStrategies),
+ remove => target.RemoveHandler(routedEvent, remove));
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Helpers/FlyoutCloseEnforcer.cs
+using System.ComponentModel;
+using Avalonia.Controls.Primitives;
+
+namespace WalletWasabi.Fluent.Helpers;
+
+public class FlyoutCloseEnforcer : IDisposable
+{
+ private readonly FlyoutBase _flyout;
+ private bool _isForcedOpen;
+
+ public FlyoutCloseEnforcer(FlyoutBase flyout)
+ {
+ _flyout = flyout;
+ }
+
+ public bool IsForcedOpen
+ {
+ get => _isForcedOpen;
+ set
+ {
+ if (_isForcedOpen == value)
+ {
+ return;
+ }
+
+ _isForcedOpen = value;
+
+ if (_isForcedOpen)
+ {
+ _flyout.Closing += RejectClose;
+ }
+ else
+ {
+ _flyout.Closing -= RejectClose;
+ }
+ }
+ }
+
+ private static void RejectClose(object? sender, CancelEventArgs e)
+ {
+ e.Cancel = true;
+ }
+
+ public void Dispose()
+ {
+ if (!IsForcedOpen)
+ {
+ _flyout.Closing -= RejectClose;
+ }
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Helpers/FlyoutController.cs
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+
+namespace WalletWasabi.Fluent.Helpers;
+
+public class FlyoutController : IDisposable
+{
+ private readonly FlyoutBase _flyout;
+ private readonly Control _parent;
+ private bool _isOpen;
+ private readonly FlyoutCloseEnforcer _enforcer;
+
+ public FlyoutController(FlyoutBase flyout, Control parent)
+ {
+ _flyout = flyout;
+ _parent = parent;
+ _enforcer = new FlyoutCloseEnforcer(flyout);
+ }
+
+ public bool IsOpen
+ {
+ get => _isOpen;
+ set
+ {
+ if (_isOpen == value)
+ {
+ return;
+ }
+
+ ToggleFlyout(value);
+ _isOpen = value;
+ }
+ }
+
+ private void ToggleFlyout(bool isVisible)
+ {
+ if (isVisible)
+ {
+ ShowFlyout();
+ }
+ else
+ {
+ HideFlyout();
+ }
+ }
+
+ private void HideFlyout()
+ {
+ _enforcer.IsForcedOpen = false;
+ _flyout.Hide();
+ }
+
+ private void ShowFlyout()
+ {
+ _enforcer.IsForcedOpen = true;
+ _flyout.ShowAt(_parent);
+ }
+
+ public void Dispose()
+ {
+ _enforcer.Dispose();
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Views/SearchBar/SearchBar.axaml
WalletWasabi.Fluent/Views/SearchBar/SearchBar.axaml
xmlns:searchBar="clr-namespace:WalletWasabi.Fluent.ViewModels.SearchBar"
xmlns:behaviors="clr-namespace:WalletWasabi.Fluent.Behaviors"
xmlns:avalonia="clr-namespace:Zafiro.Avalonia;assembly=Zafiro.Avalonia"
+ xmlns:newFolder="clr-namespace:WalletWasabi.Fluent.Behaviors.NewFolder"
x:CompileBindings="True"
x:DataType="searchBar:SearchBarViewModel"
x:Class="WalletWasabi.Fluent.Views.SearchBar.SearchBar">
</FlyoutBase.AttachedFlyout>
<i:Interaction.Behaviors>
- <avalonia:ShowAttachedFlyoutWhenFocusedBehavior IsFlyoutOpen="{Binding IsSearchListVisible, Mode=TwoWay}" />
+ <behaviors:ShowAttachedFlyoutWhenFocusedBehavior IsFlyoutOpen="{Binding IsSearchListVisible, Mode=TwoWay}" />
</i:Interaction.Behaviors>
</TextBox>

MODIFY
WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
<PackageReference Include="Avalonia.Controls.TreeDataGrid" Version="0.10.14" />
<PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" Condition="'$(Configuration)' == 'Debug'" />
- <PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.13.1" />
+ <PackageReference Include="Avalonia.Xaml.Behaviors" Version="0.10.15" />
<PackageReference Include="Avalonia.Skia" Version="$(AvaloniaVersion)" />
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<ItemGroup>
<ProjectReference Include="..\WalletWasabi\WalletWasabi.csproj" />
<ProjectReference Include="..\WalletWasabi.Fluent.Generators\WalletWasabi.Fluent.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
- <ProjectReference Include="..\Zafiro.Avalonia\Zafiro.Avalonia\Zafiro.Avalonia.csproj" />
</ItemGroup>
<ItemGroup>

MODIFY
WalletWasabi.Fluent/packages.lock.json
WalletWasabi.Fluent/packages.lock.json
},
"Avalonia.Xaml.Behaviors": {
"type": "Direct",
- "requested": "[0.10.13.1, )",
- "resolved": "0.10.13.1",
- "contentHash": "Z+wcwoJCKBBds6aHQ7Fddph3027h9tXswlrOxVh7dJEqTUK0MNYHUlyjOaqIOGl2ng3uBK6cs2L2wXG5Cwzf5g==",
+ "requested": "[0.10.15, )",
+ "resolved": "0.10.15",
+ "contentHash": "jbxlCjdIa49tSYsYfo54NJcngzhSXpvhs+pWymQjqfKcWBS6yT4CLK6U/nyquYT1XlHdyXJCi/M/R2Xn+rtI7Q==",
"dependencies": {
- "Avalonia": "0.10.13",
- "Avalonia.Xaml.Interactions": "0.10.13.1",
- "Avalonia.Xaml.Interactivity": "0.10.13.1"
+ "Avalonia": "0.10.15",
+ "Avalonia.Xaml.Interactions": "0.10.15",
+ "Avalonia.Xaml.Interactivity": "0.10.15"
}
},
"System.Drawing.Common": {
},
"Avalonia.Xaml.Interactions": {
"type": "Transitive",
- "resolved": "0.10.13.1",
- "contentHash": "FfBzAwH9TXVk3P+diK8V0t/dYufXCm1tpY7Ok14bll3xMlI+vICObxE7yzXriFTfQO7oaFONaPgdspQnfGKwEg==",
+ "resolved": "0.10.15",
+ "contentHash": "7580+UOzU8vVVJ1qH2BS+lKtJLToO1cezy29FhDGuFwVuPN8M3aYkq6LlAD5NBo2xkeaOHm7/JqTTyWbVJLOKA==",
"dependencies": {
- "Avalonia": "0.10.13",
- "Avalonia.Xaml.Interactivity": "0.10.13.1"
+ "Avalonia": "0.10.15",
+ "Avalonia.Xaml.Interactivity": "0.10.15"
}
},
"Avalonia.Xaml.Interactivity": {
"type": "Transitive",
- "resolved": "0.10.14",
- "contentHash": "/AtmTGM+QAjY+8RCkeAbRP5QcEfoWKSDsJYNGnoikRR0xRXlbONvkzhnR0ukxjWkf0NCUh6Sn2dy/HkqWP4y7Q==",
+ "resolved": "0.10.15",
+ "contentHash": "FO6Ob/fsSK02chQNB79BouAjB9o7qBg1cCcQDjhCQzRc04yE998+cpfMFpkL9ODbnUW1YcDTSKDTW0ZjTX4asQ==",
"dependencies": {
- "Avalonia": "0.10.14"
+ "Avalonia": "0.10.15"
}
},
"DynamicData": {
"NBitcoin": "7.0.6",
"NBitcoin.Secp256k1": "3.0.1"
}
- },
- "zafiro.avalonia": {
- "type": "Project",
- "dependencies": {
- "Avalonia": "0.10.15",
- "Avalonia.Xaml.Interactivity": "0.10.14"
- }
}
},
"net6.0/linux-arm64": {

MODIFY
WalletWasabi.sln
WalletWasabi.sln
@@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WalletWasabi.Fluent.Generat
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WalletWasabi.Fluent", "WalletWasabi.Fluent\WalletWasabi.Fluent.csproj", "{1E9A8055-DC14-4C7D-8878-887D50B55A10}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zafiro.Avalonia", "Zafiro.Avalonia\Zafiro.Avalonia\Zafiro.Avalonia.csproj", "{33004FCF-6BFB-4571-95B0-9712A74A1704}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -104,14 +102,6 @@ Global
{1E9A8055-DC14-4C7D-8878-887D50B55A10}.Release|Any CPU.Build.0 = Release|Any CPU
{1E9A8055-DC14-4C7D-8878-887D50B55A10}.Release|x64.ActiveCfg = Release|Any CPU
{1E9A8055-DC14-4C7D-8878-887D50B55A10}.Release|x64.Build.0 = Release|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Debug|x64.ActiveCfg = Debug|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Debug|x64.Build.0 = Debug|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Release|Any CPU.Build.0 = Release|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Release|x64.ActiveCfg = Release|Any CPU
- {33004FCF-6BFB-4571-95B0-9712A74A1704}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

DELETE
Zafiro.Avalonia
-Subproject commit 49c225adf46d6812f3374e7cf1cdaf1e88d13602

Without submodule