Skip to content
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

Add an event so that users can detect when an Application icon is clicked #14106

Merged
merged 14 commits into from
Jan 10, 2024

Conversation

danwalmsley
Copy link
Member

@danwalmsley danwalmsley commented Jan 4, 2024

On OSX when using ShutdownMode.OnExplicitShutdown, and the application has all its windows closed, the application icon remains in the Dock.

Most end-users expect that clicking the Dock icon will re-open the main window of the application. (See safari and other similar apps on MacOS).

However since there is no way for an Avalonia application to detect or handle this event, Avalonia applications can not implement this behaviour.

Applications can also not detect when the Dock icon is right clicked-> Hide or right clicked and Show... where the application is placed into a background state on macOS.

This PR adds an Application.Clicked event which only gets triggered on MacOS when the Dock icon (which represents the Application and not a Window) is clicked.

This PR adds an IActivatableLifetime that provides Activated and Deactivated events with event args providing the reason.

This api covers all bases for the specified scenarios on OSX and the api can be used as follows:

public override void OnFrameworkInitializationCompleted()
        {
            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
            {
                desktopLifetime.MainWindow = new MainWindow();
                desktopLifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown;
            }

            if (ApplicationLifetime is IActivatableApplicationLifetime activatableApplicationLifetime)
            {
                activatableApplicationLifetime.Activated += ActivatableApplicationLifetimeOnActivated;
                
                activatableApplicationLifetime.Deactivated += ActivatableApplicationLifetimeOnDeactivated;
            }
        }

        private void ActivatableApplicationLifetimeOnActivated(object sender, ActivatedEventArgs e)
        {
            if (e.Kind == ActivationKind.Reopen)
            {
                if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
                {
                    if (desktopLifetime.Windows.Count == 0)
                    {
                        desktopLifetime.MainWindow = new MainWindow();
                        desktopLifetime.MainWindow.Show();
                    }
                }
            }
            else if (e.Kind == ActivationKind.OpenUri && e is ProtocolActivatedEventArgs pargs)
            {
                Console.WriteLine($"Open uri {pargs.Uri}");
            }
            else if (e.Kind == ActivationKind.Background)
            {
                Console.WriteLine("Unhidden");
            }
        }
        
        private void ActivatableApplicationLifetimeOnDeactivated(object sender, ActivatedEventArgs e)
        {
            if (e.Kind == ActivationKind.Background)
            {
                Console.WriteLine("Hidden");
            }
        }

This allows the developer to handle this in any way and implement the behaviour they wish for their application.

@thevortexcloud
Copy link
Contributor

This PR adds an Application.Clicked

Should it be called something else so it's consistent with the rest of the Avalonia events? EG PointerPressed

@maxkatz6
Copy link
Member

maxkatz6 commented Jan 6, 2024

I think we are good with one of two possible APIs:

  1. Something that follows UWP-like API:
public enum ActivationKind
{
    Reopen = 10, // Dock?
}
public class ActivatedEventArgs : EventArgs
{
    public ActivationKind Kind {get;}
}

public class Application
{
    public event EventHandler<ActivatedEventArgs> Activated;
}
  1. Or MacOS.HandleReopenEvent attached event, specific to MacOS. And following Platform-specific APIs. #11384. This attached event also wouldn't be a big API blocker later, if we decide to use first approach instead at some point.

@workgroupengineering
Copy link
Contributor

workgroupengineering commented Jan 6, 2024

I think we are good with one of two possible APIs:

1. Something that follows UWP-like API:
public enum ActivationKind
{
    Reopen = 10, // Dock?
}
public class ActivatedEventArgs : EventArgs
{
    public ActivationKind Kind {get;}
}

public class Application
{
    public event EventHandler<ActivatedEventArgs> Activated;
}

I think it's the best choice, I think we should also migrate IApplicationPlatformEvents.

@maxkatz6
Copy link
Member

maxkatz6 commented Jan 6, 2024

@workgroupengineering yes, it would make sense there too

@danwalmsley
Copy link
Member Author

@maxkatz6 @workgroupengineering, had an offline discussion with @maxkatz6,

will implement the above suggestion, but making it part of the application lifetime interface.

@danwalmsley
Copy link
Member Author

Refactored using lifetimes and updated the description to show api usage.

{
event EventHandler<ActivatedEventArgs> Activated;

event EventHandler<ActivatedEventArgs> Deactivated;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should split these args, and add DeactivatedEventArgs instead of reusing. Maybe even with DeactivationKind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I interpreted correctly what @danwalmsley meant if ActivationKind.Background occurs in the Activated event, it indicates that the app has been reactivated from suspension, otherwise in Deactivated it indicates that the app has been suspended. So for each of the elements of the enum.

If so I would suggest renaming:
ActivatedEventArgs in ApplicationStateEventArgs
ActivationKind in ApplicationStateKind

and create

ApplicationStateActivatedEventArgs: ApplicationStateEventArgs
ApplicationStateDeactivatedEventArgs: ApplicationStateEventArgs

cc @robloo

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043371-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043375-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

This allows the dock icon to be kept in sync so its menu options there say "Hide" / "Show" correctly.
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043383-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043398-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043430-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 added os-browser os-ios os-android backport-candidate-11.0.x Consider this PR for backporting to 11.0 branch feature labels Jan 10, 2024
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.1.999-cibuild0043438-beta. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 disabled auto-merge January 10, 2024 08:06
@maxkatz6 maxkatz6 merged commit 0e014f9 into master Jan 10, 2024
5 of 7 checks passed
@maxkatz6 maxkatz6 deleted the features/osx-allow-app-to-reopen-main-window branch January 10, 2024 08:06
@maxkatz6 maxkatz6 added backported-11.0.x and removed backport-candidate-11.0.x Consider this PR for backporting to 11.0 branch labels Jan 17, 2024
maxkatz6 pushed a commit that referenced this pull request Jan 17, 2024
…cked (#14106)

* Add an event so that users can detect when an Application icon is clicked.

* refactor to use Lifetime apis.

* use ActivationKind instead of reason to be consistent with other xaml platforms

* implement macos raise url events.

* add docs.

* add apis to programatically Activate and Deactivate the application.

This allows the dock icon to be kept in sync so its menu options there say "Hide" / "Show" correctly.

* fix api naming.

* Add Browser IActivatableApplicationLifetime impl

* Implement IActivatableApplicationLifetime on Android

* Add IActivatableApplicationLifetime iOS implementation

* Adjust android impl a little

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
#Conflicts:
#	src/Browser/Avalonia.Browser/Interop/InputHelper.cs
#	src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
Takoooooo added a commit that referenced this pull request Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants