diff --git a/Mopups/Mopups.Maui/Mopups.csproj b/Mopups/Mopups.Maui/Mopups.csproj index ba5cb26..7aff78f 100644 --- a/Mopups/Mopups.Maui/Mopups.csproj +++ b/Mopups/Mopups.Maui/Mopups.csproj @@ -13,7 +13,10 @@ Popups for MAUI with multi-window support. Fork of Mopups by LuckyDucko. https://git.marketally.com/marketally/MarketAlly.Mopups https://git.marketally.com/marketally/MarketAlly.Mopups - MIT + BSD-3-Clause + Copyright (c) 2022, Tyson Elliot Hooker. All rights reserved. + icon.png + README.md true 21.0 11.0 @@ -104,4 +107,9 @@ + + + + + diff --git a/Mopups/Mopups.Maui/icon.png b/Mopups/Mopups.Maui/icon.png new file mode 100644 index 0000000..efdc7c3 Binary files /dev/null and b/Mopups/Mopups.Maui/icon.png differ diff --git a/README.md b/README.md index a5b34f0..e25c35d 100644 --- a/README.md +++ b/README.md @@ -1,175 +1,290 @@ +# MarketAlly.Mopups +[![NuGet Version](https://img.shields.io/nuget/v/MarketAlly.Mopups.svg?style=flat)](https://www.nuget.org/packages/MarketAlly.Mopups/) +[![NuGet Downloads](https://img.shields.io/nuget/dt/MarketAlly.Mopups.svg)](https://www.nuget.org/packages/MarketAlly.Mopups/) +[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![.NET](https://img.shields.io/badge/.NET-9.0-512BD4)](https://dotnet.microsoft.com/download) +[![Platform](https://img.shields.io/badge/Platform-iOS%20%7C%20Android%20%7C%20Windows%20%7C%20macOS-lightgray)](https://dotnet.microsoft.com/apps/maui) - -
-

-

Mopups

-

- Customisable Popups for MAUI -
-

-

- - - - +Customizable popups for .NET MAUI with multi-window support. A fork of [Mopups](https://github.com/LuckyDucko/Mopups) by Tyson Hooker (LuckyDucko) with enhanced multi-window capabilities for all platforms. ## Table of Contents -[![NuGet Downloads](https://img.shields.io/nuget/dt/Mopups.svg)](https://www.nuget.org/packages/Mopups/) -[![nuget](https://img.shields.io/nuget/v/Mopups.svg)](https://www.nuget.org/packages/Mopups/) -* [About the Project](#about-the-project) -* [Getting Started](#getting-started) - * [Installation](#installation) -* [Usage](#usage) -* [License](#license) -* [Contact](#contact) +- [Features](#features) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Multi-Window Support](#multi-window-support) +- [Usage](#usage) +- [Platform Support](#platform-support) +- [Requirements](#requirements) +- [License](#license) +- [Acknowledgments](#acknowledgments) +## Features +### Core Capabilities (from Mopups) - -## About The Project +- **Customizable Popups**: Full control over popup appearance and behavior +- **Async/Await Pattern**: Modern asynchronous popup handling +- **Animation Support**: Built-in appearing and disappearing animations +- **System Padding**: Automatic safe area handling +- **Back Button Handling**: Customizable back button behavior +- **Cross-Platform**: iOS, Android, Windows, and macOS (Mac Catalyst) -Mopups is a replacement for the "Rg.Plugins.Popups" plugin for Xamarin. Mopups intends to provide a similar experience to this plugin, however also clean up the code base and provide forward looking enhancements. Developers familar with the original plugin should find it a smooth transition, but we do recommend reading the wiki and reaching out with any issues. +### New in MarketAlly.Mopups v2.0.0 -The "PreBaked" is a neat blend of Mopups and AsyncAwaitBestPractices plugins to bring you a quick way to add popups into your MAUIs App using familiar concepts +- **Multi-Window Support**: Display popups on specific windows instead of always on the main window + - Windows: Uses target window's `XamlRoot` + - iOS/iPadOS: Uses target window's `UIWindowScene` + - macOS Catalyst: Uses target window's `UIWindowScene` + - Android: Uses target window's Activity `DecorView` -Platforms Supported (Current) -- Android -- iOS -- Windows -- MacOS (Mac Catalyst) (This is a bit iffy..) +## Installation +Install via NuGet Package Manager: -Below is a video by @jfversluis introducing Mopups -[![Watch the video](https://img.youtube.com/vi/OGWhgASmSto/hqdefault.jpg)](https://youtu.be/OGWhgASmSto) - - - -## Getting Started - -First, you must follow the [initialisation](https://github.com/LuckyDucko/Mopups/wiki/Setup) - -### Installation - -You can install the nuget by looking up 'Mopups' in your nuget package manager, or by getting it [here](https://www.nuget.org/packages/Mopups/) - - - - -## Usage -here is an example of what this plugin makes easy (Looks slow due to giphy) - -![Gif Example](https://j.gifs.com/xn4mw9.gif) - - -### New Example -To Use the plugin for its inbuilt popup pages in a basic setting (Dual/Single Response, Login, TextInput EntryInput,and loader.) All you need are these one liners - -`SingleResponse Popup Page` -```csharp -return await SingleResponseViewModel.AutoGenerateBasicPopup(Color.HotPink, Color.Black, "I Accept", Color.Gray, "Good Job, enjoy this single response example", "thumbsup.png"); +```bash +dotnet add package MarketAlly.Mopups ``` -`DualResponse Popup Page` -```csharp -return await DualResponseViewModel.AutoGenerateBasicPopup(Color.WhiteSmoke, Color.Red, "Okay", Color.WhiteSmoke, Color.Green, "Looks Good!", Color.DimGray, "This is an example of a dual response popup page", "thumbsup.png"); +Or via Package Manager Console: + +```powershell +Install-Package MarketAlly.Mopups ``` -`Loader Popup Page` -```csharp - await PreBakedMopupService.GetInstance().WrapTaskInLoader(Task.Delay(10000), Color.Blue, Color.White, LoadingReasons(), Color.Black); -``` +## Quick Start -`Text Input PopupPage` -```csharp -await TextInputViewModel.AutoGenerateBasicPopup(Color.WhiteSmoke, Color.Red, "Cancel", Color.WhiteSmoke, Color.Green, "Submit", Color.DimGray, "Text input Example", string.Empty); -``` -`Entry Input PopupPage` -```csharp -await EntryInputViewModel.AutoGenerateBasicPopup(Color.WhiteSmoke, Color.Red, "Cancel", Color.WhiteSmoke, Color.Green, "Submit", Color.DimGray, "Text input Example", string.Empty); -``` +### 1. Configure in MauiProgram.cs -`LoginPage PopupPage` ```csharp -var (username, password) = await LoginViewModel.AutoGenerateBasicPopup(Color.WhiteSmoke, Color.Red, "Cancel", Color.WhiteSmoke, Color.Green, "Submit", Color.DimGray, string.Empty, "Username Here", string.Empty, "Password here", "thumbsup.png", 0, 0); -``` +using Mopups.Hosting; -or, to return from the loader a value -```csharp -await PreBakedMopupService.GetInstance().WrapReturnableTaskInLoader(IndepthCheckAgainstDatabase(), Color.Blue, Color.White, LoadingReasons(), Color.Black); -``` - -you can also add in synchronous functions, however they are wrapped in a task -```csharp - -private bool LongRunningFunction(int millisecondDelay) +public static MauiApp CreateMauiApp() { - Thread.Sleep(millisecondDelay); - return true; -} -await PreBakedMopupService.GetInstance().WrapReturnableFuncInLoader(LongRunningFunction, 6000, Color.Blue, Color.White, LoadingReasons(), Color.Black); + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureMopups(); // Add this line + return builder.Build(); +} ``` -## That's it! for advanced usage read on +### 2. Create a Popup Page -In Version 1.2.0, Mopups has added some pre created pages that can provide users the ability to return data from popups. I have also added the ability to overload the look of these pages and create your own. +```csharp +using Mopups.Pages; -I do wish it were simpler, however, with the limited time i have to work on this, it'll have to do. +public class MyPopupPage : PopupPage +{ + public MyPopupPage() + { + Content = new StackLayout + { + Children = + { + new Label { Text = "Hello from popup!" }, + new Button { Text = "Close", Command = new Command(async () => + await MopupService.Instance.PopAsync()) } + } + }; + } +} +``` -This set of API's will be used for when the basic API wont cut it, without relying on me making another overload for every situation under the sun. +### 3. Show the Popup -This API introduces +```csharp +using Mopups.Services; - `GeneratePopup` -Which allows you to supply your own popuppage xaml which will then be attached to whatever VM you called it from. +// Show on main window (default behavior) +await MopupService.Instance.PushAsync(new MyPopupPage()); -`GeneratePopup(Dictionary propertyDictionary)` -Which allows you have a dictionary of values that a popup uses, pass and automatically attach to the appropriate properties on the VM side +// Show on specific window (NEW in v2.0.0) +await MopupService.Instance.PushAsync(new MyPopupPage(), targetWindow); +``` -These are both non-static. and require you to have an instance of the ViewModel to work with. Hence +## Multi-Window Support -`.GenerateVM()` -Which provides you with a new instance of that VM +The key enhancement in MarketAlly.Mopups is the ability to display popups on specific windows. This is essential for applications with multiple windows, such as: -`.PullViewModelProperties()` -Which collects all the properties of a VM, and provides them to you in a dictionary, so you can reuse and also while debugging, check what exists/whats been changed -Returns this `Dictionary ` +- Helper/tool windows +- Document windows +- Settings panels +- Floating utility windows -However, for initialisation, i internally (and you can use) the following function -`.InitialiseOptionalProperties(Dictionary optionalProperties)` -Which will attempt to set each of the viewmodel properties with the corrosponding value in the dictionary +### API -So, to fix that, i provide -`.FinalisePreparedProperties(Dictionary viewModelProperties)` -Which takes in the `Dictionary ` and creates `Dictionary optionalProperties` +```csharp +// Standard push (uses main window) +Task PushAsync(PopupPage page, bool animate = true); +// Window-aware push (NEW) +Task PushAsync(PopupPage page, Window? window, bool animate = true); +``` +### Example: Showing Dialog on Helper Window -**If you want to make your own Popup Page** +```csharp +public class HelperWindow : Window +{ + public async Task ShowConfirmation() + { + var popup = new ConfirmationPopup(); -This is the real power of this Plugin . If you look at the source for DualResponsePopupPage, or the SingleResponse version you'll notice that they are just simple Xaml Pages. Nothing fancy. + // Pass 'this' to show popup on this window, not the main window + await MopupService.Instance.PushAsync(popup, this); + } +} +``` -You can create the full thing yourself -1. Create Xaml Page with codebehind -2. Create your ViewModel that is associated with the popup, lets call ours `InformationPopupPage` -3. Ensure your ViewModel inherits from `PopupViewModel` where `TReturnable` is what you want the popuppage to return to its caller -4. Ensure that your xaml page codebehind inherits from `PopupPage` (requirement to use rg plugins popup) and `IGenericViewModel` where `TViewModel` is your Viewmodel, in our case it will be `IGenericViewModel` -5. You're ready to start using it the same as `DualResponsePopupPage` +### Platform Implementation Details -or you can provide your own Xaml Page, with a codebehind that inherits from `PopupPage` and `IGenericViewModel` where `TViewModel` is the plugin provided VM you wish to use. +| Platform | How Window is Used | +|----------|-------------------| +| Windows | Gets `XamlRoot` from the target window's native `Microsoft.UI.Xaml.Window` | +| iOS/iPadOS | Gets `UIWindowScene` from the target window's native `UIWindow` | +| macOS Catalyst | Gets `UIWindowScene` from the target window's native `UIWindow` | +| Android | Gets `DecorView` from the target window's `Activity` | -to use this version, just call `TViewModel.GeneratePopup()` +## Usage +### Basic Popup Operations +```csharp +// Push a popup +await MopupService.Instance.PushAsync(new MyPopupPage()); + +// Push with animation control +await MopupService.Instance.PushAsync(new MyPopupPage(), animate: false); + +// Pop the topmost popup +await MopupService.Instance.PopAsync(); + +// Pop all popups +await MopupService.Instance.PopAllAsync(); + +// Remove specific popup +await MopupService.Instance.RemovePageAsync(specificPopup); + +// Check popup stack +var popupStack = MopupService.Instance.PopupStack; +``` + +### Events + +```csharp +MopupService.Instance.Pushing += (sender, args) => + Console.WriteLine($"Pushing: {args.Page}"); + +MopupService.Instance.Pushed += (sender, args) => + Console.WriteLine($"Pushed: {args.Page}"); + +MopupService.Instance.Popping += (sender, args) => + Console.WriteLine($"Popping: {args.Page}"); + +MopupService.Instance.Popped += (sender, args) => + Console.WriteLine($"Popped: {args.Page}"); +``` + +### PopupPage Properties + +```csharp +public class MyPopupPage : PopupPage +{ + public MyPopupPage() + { + // Background click dismisses popup + CloseWhenBackgroundIsClicked = true; + + // Handle safe areas + HasSystemPadding = true; + + // Background color + BackgroundColor = Color.FromRgba("#80000000"); + } + + // Override for custom back button behavior + protected override bool OnBackButtonPressed() + { + // Return true to prevent dismissal + return false; + } +} +``` + +## Platform Support + +| Platform | Minimum Version | Multi-Window Support | +|----------|-----------------|---------------------| +| iOS | 11.0 | Yes (iOS 13+ with UIScene) | +| Android | API 21 (5.0) | Yes (Android 7.0+ multi-window) | +| Windows | 10.0.17763.0 | Yes | +| macOS | 13.1 (Catalyst) | Yes | + +## Requirements + +- .NET 9.0 +- Microsoft.Maui.Controls 9.0+ +- Microsoft.Maui.Essentials 9.0+ - ## License -This project uses the MIT License - -## Contact -My [Github](https://github.com/LuckyDucko), +This project is licensed under the **BSD 3-Clause License**. +``` +BSD 3-Clause License +Copyright (c) 2022, Tyson Elliot Hooker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +## Acknowledgments + +This project is a fork of [Mopups](https://github.com/LuckyDucko/Mopups), originally created by **Tyson Hooker (LuckyDucko)**. We extend our gratitude to: + +- **Tyson Hooker** - Original author and maintainer of Mopups +- **Maksym Koshovyi** - Contributor +- **Aswin P G** - Contributor +- **Kirill Lyubimov** - Contributor +- **Martijn Van Dijk** - Contributor +- **Shane Neuville** - Contributor + +Mopups itself was created as a replacement for the "Rg.Plugins.Popups" plugin for Xamarin, providing a modern popup solution for .NET MAUI. + +### Additional Resources + +- [Original Mopups Repository](https://github.com/LuckyDucko/Mopups) +- [Mopups Introduction Video by Gerald Versluis](https://youtu.be/OGWhgASmSto) + +--- + +**Built with precision by [MarketAlly](https://marketally.com)** + +*Multi-window popup support for .NET MAUI applications.*