-
-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch to ISingleViewApplicationLifetime #269
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
a78caa5
Switch to ISingleViewApplicationLifetime
tomlm 4cee475
Merge branch 'main' into tomlm/singleLifetime
tomlm 5be6d2e
marge master and code review corrections
tomlm 476a7ef
lint
tomlm 2cb3547
lint
tomlm 5305b50
lint lintlint
tomlm b8d76e8
lint
tomlm a0a261d
lint
tomlm ae8a1b5
I am developing a deep hatred of resharper.
tomlm f4aeea4
Automated JetBrains cleanup
github-actions[bot] 594c78f
update template
tomlm 4840eb0
merge
tomlm 84f5053
fix build break
tomlm 9622f19
Automated JetBrains cleanup
github-actions[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Controls.ApplicationLifetimes; | ||
using Avalonia.Platform; | ||
using Avalonia.Threading; | ||
using Consolonia.Core.Drawing.PixelBufferImplementation; | ||
using Consolonia.Core.Infrastructure; | ||
|
||
// ReSharper disable CheckNamespace | ||
// ReSharper disable NotNullOrRequiredMemberIsNotInitialized | ||
// ReSharper disable ConstantConditionalAccessQualifier | ||
namespace Consolonia | ||
{ | ||
public class ConsoloniaLifetime : ISingleViewApplicationLifetime, | ||
IControlledApplicationLifetime, | ||
ISingleTopLevelApplicationLifetime, | ||
IDisposable | ||
{ | ||
private CancellationTokenSource _cts = new(); | ||
private bool _disposedValue; | ||
private int _exitCode; | ||
private bool _isShuttingDown; | ||
|
||
/// <summary> | ||
/// Gets the arguments passed to the AppBuilder Start method. | ||
/// </summary> | ||
#pragma warning disable CA1819 // Properties should not return arrays | ||
public string[] Args { get; set; } | ||
#pragma warning restore CA1819 // Properties should not return arrays | ||
|
||
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup; | ||
|
||
public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit; | ||
|
||
public void Shutdown(int exitCode = 0) | ||
{ | ||
DoShutdown(new ShutdownRequestedEventArgs(), true, true, exitCode); | ||
} | ||
|
||
public TopLevel TopLevel { get; set; } | ||
|
||
public Control MainView | ||
{ | ||
get => (Control)TopLevel.Content; | ||
tomlm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
set | ||
{ | ||
if (TopLevel == null) TopLevel = new Window(); | ||
TopLevel.Content = value; | ||
} | ||
} | ||
|
||
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested; | ||
|
||
public bool TryShutdown(int exitCode = 0) | ||
{ | ||
return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode); | ||
} | ||
|
||
internal void SetupCore(string[] args) | ||
{ | ||
Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); | ||
|
||
var lifetimeEvents = AvaloniaLocator.Current.GetService<IPlatformLifetimeEventsImpl>(); | ||
|
||
if (lifetimeEvents != null) | ||
tomlm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
lifetimeEvents.ShutdownRequested += OnShutdownRequested; | ||
|
||
TopLevel.Closed += (_, _) => TryShutdown(); | ||
} | ||
|
||
public int Start(string[] args) | ||
{ | ||
return StartCore(args); | ||
} | ||
|
||
/// <summary> | ||
/// Since the lifetime must be set up/prepared with 'args' before executing Start(), an overload with no parameters | ||
/// seems more suitable for integrating with some lifetime manager providers, such as MS HostApplicationBuilder. | ||
/// </summary> | ||
/// <returns>exit code</returns> | ||
public int Start() | ||
{ | ||
return StartCore(Args ?? Array.Empty<string>()); | ||
} | ||
|
||
internal int StartCore(string[] args) | ||
{ | ||
SetupCore(args); | ||
|
||
tomlm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(TopLevel as Window)?.Show(); | ||
|
||
Dispatcher.UIThread.MainLoop(_cts.Token); | ||
Environment.ExitCode = _exitCode; | ||
return _exitCode; | ||
} // ReSharper disable UnusedParameter.Local | ||
// ReSharper disable UnusedMember.Local | ||
#pragma warning disable IDE0060 // Remove unused parameter | ||
private bool DoShutdown( | ||
ShutdownRequestedEventArgs e, | ||
bool isProgrammatic, | ||
bool force = false, | ||
int exitCode = 0) | ||
{ | ||
if (!force) | ||
{ | ||
ShutdownRequested?.Invoke(this, e); | ||
|
||
if (e.Cancel) | ||
return false; | ||
|
||
if (_isShuttingDown) | ||
throw new InvalidOperationException("Application is already shutting down."); | ||
} | ||
|
||
_exitCode = exitCode; | ||
_isShuttingDown = true; | ||
|
||
var consoleWindow = (ConsoleWindow)TopLevel.PlatformImpl; | ||
consoleWindow.Console.RestoreConsole(); | ||
|
||
tomlm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
try | ||
{ | ||
var args = new ControlledApplicationLifetimeExitEventArgs(exitCode); | ||
Exit?.Invoke(this, args); | ||
_exitCode = args.ApplicationExitCode; | ||
} | ||
finally | ||
{ | ||
_isShuttingDown = false; | ||
|
||
_cts?.Cancel(); | ||
_cts = null; | ||
Dispatcher.UIThread.InvokeShutdown(); | ||
} | ||
|
||
return true; | ||
} | ||
#pragma warning restore IDE0060 // Remove unused parameter | ||
|
||
// ReSharper disable once UnusedMember.Local | ||
private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) | ||
{ | ||
DoShutdown(e, false); | ||
} | ||
|
||
/// <summary> | ||
/// returned task indicates that console is successfully paused | ||
/// </summary> | ||
public Task DisconnectFromConsoleAsync(CancellationToken cancellationToken) | ||
{ | ||
var taskToWaitFor = new TaskCompletionSource(); | ||
cancellationToken.Register(() => taskToWaitFor.SetResult()); | ||
|
||
var mainWindowPlatformImpl = (ConsoleWindow)TopLevel.PlatformImpl; | ||
IConsole console = mainWindowPlatformImpl!.Console; | ||
|
||
tomlm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Task pauseTask = taskToWaitFor.Task; | ||
|
||
console.PauseIO(pauseTask); | ||
|
||
pauseTask.ContinueWith(_ => | ||
{ | ||
mainWindowPlatformImpl.Console.ClearScreen(); | ||
|
||
Dispatcher.UIThread.Post(() => { MainView.InvalidateVisual(); }); | ||
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); | ||
|
||
return Dispatcher.UIThread.InvokeAsync(() => { }).GetTask(); | ||
} | ||
|
||
#pragma warning disable CA1822 | ||
// ReSharper disable once MemberCanBeMadeStatic.Global It can be static only because we rely on static locator currently. I bet it will change in the future | ||
public bool IsRgbColorMode() | ||
#pragma warning restore CA1822 | ||
{ | ||
IConsoleColorMode consoleColorMode = AvaloniaLocator.Current.GetService<IConsoleColorMode>() | ||
?? throw new ConsoloniaException( | ||
"Console color mode has not been initialized"); | ||
|
||
return consoleColorMode is RgbConsoleColorMode; | ||
} | ||
|
||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (!_disposedValue) | ||
{ | ||
if (disposing) | ||
{ | ||
// TODO: dispose managed state (managed objects) | ||
_cts?.Dispose(); | ||
_cts = null; | ||
} | ||
|
||
// TODO: free unmanaged resources (unmanaged objects) and override finalizer | ||
// TODO: set large fields to null | ||
_disposedValue = true; | ||
} | ||
} | ||
|
||
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources | ||
// ~ConsoloniaLifetime() | ||
// { | ||
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||
// Dispose(disposing: false); | ||
// } | ||
|
||
public void Dispose() | ||
{ | ||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be we should propose Avalonia to split their implementation of single view lifetime into core packages just to re-use that among platforms. Sounds meaningful?