Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f58aac9987
|
No files matched your search
@@ -53,6 +53,12 @@ its build targets) and copies its brand assets (`Assets/Brand/app-icon.svg`,
|
||||
|
||||
## Usage
|
||||
|
||||
The default sample/starter Help menus include **View on GitHub**,
|
||||
**Submit an issue…** and **Community support…**, matching gclo. Derive the issue
|
||||
chooser (`/issues/new/choose`) and Discussions (`/discussions`) from the app's
|
||||
`AppInfo.RepositoryUrl`; enable GitHub Discussions during repository setup.
|
||||
Both WinUI and WPF samples show the wiring without requiring a new AppKit API.
|
||||
|
||||
New apps should start from the KofTwentyTwo Windows app template, which has everything
|
||||
below in place; the design is the
|
||||
[Windows desktop reference architecture](https://github.com/KofTwentyTwo/standards/blob/main/architecture/windows-desktop-app.md).
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2026 James Maes
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
|
||||
namespace AppKit.Sample.WinUI;
|
||||
|
||||
/// <summary>Opens only the app's fixed public Help destinations after deliberate native menu actions.</summary>
|
||||
public sealed partial class MainWindow
|
||||
{
|
||||
/// <summary>Shows this Windows app's repository in the user's default browser.</summary>
|
||||
private async void GitHub_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(SampleApp.Info.RepositoryUrl!);
|
||||
|
||||
|
||||
|
||||
/// <summary>Offers the app's issue templates without sending private document data.</summary>
|
||||
private async void SubmitIssue_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(SampleApp.IssuesUrl);
|
||||
|
||||
|
||||
|
||||
/// <summary>Opens community questions and tips in this app's repository.</summary>
|
||||
private async void CommunitySupport_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(SampleApp.SupportUrl);
|
||||
|
||||
|
||||
|
||||
/// <summary>A missing browser or OS launch failure is visible; no document path/content is included.</summary>
|
||||
private async Task OpenHelpPageAsync(Uri target)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!await global::Windows.System.Launcher.LaunchUriAsync(target))
|
||||
{
|
||||
StatusText.Text = "The link could not be opened. Check your default browser.";
|
||||
}
|
||||
}
|
||||
catch(Exception exception) when(exception is System.Runtime.InteropServices.COMException or InvalidOperationException or ArgumentException)
|
||||
{
|
||||
StatusText.Text = "The link could not be opened. Check your default browser.";
|
||||
App.Log.Warning(FormattableString.Invariant($"A Help link could not be opened ({exception.GetType().Name}, 0x{exception.HResult:X8})."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,11 @@
|
||||
<MenuFlyoutSeparator />
|
||||
<ToggleMenuFlyoutItem x:Name="SplashToggle" Text="Show splash at startup" Click="Splash_Click" />
|
||||
</MenuBarItem>
|
||||
<MenuBarItem Title="Help">
|
||||
<MenuBarItem Title="Help" AccessKey="H">
|
||||
<MenuFlyoutItem Text="View on GitHub" AccessKey="G" AutomationProperties.AutomationId="MenuGitHub" Click="GitHub_Click" />
|
||||
<MenuFlyoutItem Text="Submit an issue…" AccessKey="I" AutomationProperties.AutomationId="MenuSubmitIssue" ToolTipService.ToolTip="Report a bug or request a feature on GitHub" Click="SubmitIssue_Click" />
|
||||
<MenuFlyoutItem Text="Community support…" AccessKey="S" AutomationProperties.AutomationId="MenuCommunitySupport" ToolTipService.ToolTip="Ask a question or share tips in GitHub Discussions" Click="CommunitySupport_Click" />
|
||||
<MenuFlyoutSeparator />
|
||||
<MenuFlyoutItem x:Name="CheckForUpdatesMenuItem" Text="Check for updates" Click="CheckForUpdates_Click" />
|
||||
<MenuFlyoutItem x:Name="ActivityLogMenuItem" Text="Activity log" Click="ActivityLog_Click" />
|
||||
<MenuFlyoutSeparator />
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2026 James Maes (KofTwentyTwo)
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
|
||||
|
||||
namespace AppKit.Sample.Wpf;
|
||||
|
||||
/// <summary>Opens the sample's fixed public Help destinations using the Windows default browser.</summary>
|
||||
public partial class MainWindow
|
||||
{
|
||||
/// <summary>Shows the sample's source repository.</summary>
|
||||
private void GitHub_Click(object sender, RoutedEventArgs args) => OpenHelpPage(SampleApp.Info.RepositoryUrl!);
|
||||
|
||||
|
||||
|
||||
/// <summary>Offers the repository's bug/feature templates.</summary>
|
||||
private void SubmitIssue_Click(object sender, RoutedEventArgs args) => OpenHelpPage(SampleApp.IssuesUrl);
|
||||
|
||||
|
||||
|
||||
/// <summary>Opens the sample's community support destination.</summary>
|
||||
private void CommunitySupport_Click(object sender, RoutedEventArgs args) => OpenHelpPage(SampleApp.SupportUrl);
|
||||
|
||||
|
||||
|
||||
/// <summary>Surfaces OS/browser launch failure without passing private app data to the shell.</summary>
|
||||
private void OpenHelpPage(Uri target)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = Process.Start(new ProcessStartInfo(target.AbsoluteUri) { UseShellExecute = true });
|
||||
}
|
||||
catch(Exception exception) when(exception is System.ComponentModel.Win32Exception or InvalidOperationException)
|
||||
{
|
||||
StatusText.Text = "The link could not be opened. Check your default browser.";
|
||||
Program.Log.Warning(FormattableString.Invariant($"A Help link could not be opened ({exception.GetType().Name}, 0x{exception.HResult:X8})."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@
|
||||
<MenuItem x:Name="SplashToggle" Header="Show splash at startup" IsCheckable="True" Click="Splash_Click" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Help">
|
||||
<MenuItem Header="View on _GitHub" AutomationProperties.AutomationId="MenuGitHub" Click="GitHub_Click" />
|
||||
<MenuItem Header="Submit an _issue…" AutomationProperties.AutomationId="MenuSubmitIssue" ToolTip="Report a bug or request a feature on GitHub" Click="SubmitIssue_Click" />
|
||||
<MenuItem Header="Community _support…" AutomationProperties.AutomationId="MenuCommunitySupport" ToolTip="Ask a question or share tips in GitHub Discussions" Click="CommunitySupport_Click" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="Check for _updates" Click="CheckForUpdates_Click" />
|
||||
<MenuItem x:Name="ActivityLogMenuItem" Header="Activity _log" Click="ActivityLog_Click" />
|
||||
<Separator />
|
||||
|
||||
@@ -36,5 +36,17 @@ internal static class SampleApp
|
||||
|
||||
public static AppPaths Paths { get; } = new(Info);
|
||||
|
||||
|
||||
|
||||
/// <summary>The app's bug/feature templates, independent of any upstream project credited in About.</summary>
|
||||
public static Uri IssuesUrl { get; } = new(Info.RepositoryUrl!.AbsoluteUri.TrimEnd('/') + "/issues/new/choose");
|
||||
|
||||
|
||||
|
||||
/// <summary>Community questions and tips in the app's own repository.</summary>
|
||||
public static Uri SupportUrl { get; } = new(Info.RepositoryUrl!.AbsoluteUri.TrimEnd('/') + "/discussions");
|
||||
|
||||
|
||||
|
||||
public static SettingsStore<SampleSettings> SettingsStore { get; } = new(Paths.SettingsFile, SampleSettingsContext.Default.SampleSettings);
|
||||
}
|
||||
@@ -152,7 +152,12 @@ public sealed class AppSession : IDisposable
|
||||
/// expand (its flyout opens before the menu is ready), so the menu is re-opened
|
||||
/// until the item appears.
|
||||
/// </summary>
|
||||
public void InvokeMenuItem(string menuName, string itemAutomationId)
|
||||
public void InvokeMenuItem(string menuName, string itemAutomationId) => Invoke(OpenMenuItem(menuName, itemAutomationId));
|
||||
|
||||
|
||||
|
||||
/// <summary>Opens an owned native menu and returns its item without launching an external destination.</summary>
|
||||
public AutomationElement OpenMenuItem(string menuName, string itemAutomationId)
|
||||
{
|
||||
AutomationElement menu = WaitFor(
|
||||
() => MainWindow.FindFirstDescendant(cf => cf.ByControlType(ControlType.MenuItem).And(cf.ByName(menuName))),
|
||||
@@ -171,7 +176,7 @@ public sealed class AppSession : IDisposable
|
||||
Wait.UntilInputIsProcessed();
|
||||
item = Retry.WhileNull(() => FindInApp(cf => cf.ByAutomationId(itemAutomationId)), timeout: TimeSpan.FromSeconds(3), interval: s_pollInterval, ignoreException: true).Result;
|
||||
}
|
||||
Invoke(item ?? throw new TimeoutException($"Menu item '{itemAutomationId}' never appeared under '{menuName}'. (app exited: {App.HasExited})"));
|
||||
return item ?? throw new TimeoutException($"Menu item '{itemAutomationId}' never appeared under '{menuName}'. (app exited: {App.HasExited})");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,25 @@ public class SampleAppTests
|
||||
|
||||
|
||||
|
||||
/// <summary>The real Help menu exposes the app's repository, issue reporting and community commands without launching a browser.</summary>
|
||||
[Theory]
|
||||
[InlineData(SampleKind.WinUI)]
|
||||
[InlineData(SampleKind.Wpf)]
|
||||
public void Help_PublicDestinations_AreAvailableInTheNativeMenu(SampleKind kind)
|
||||
{
|
||||
using var session = new AppSession(kind);
|
||||
_ = session.OpenMenuItem("Help", "MenuGitHub");
|
||||
foreach((string identity, string label) in new[] { ("MenuGitHub", "GitHub"), ("MenuSubmitIssue", "issue"), ("MenuCommunitySupport", "support") })
|
||||
{
|
||||
AutomationElement item = session.WaitForElement(identity);
|
||||
Assert.True(item.IsEnabled);
|
||||
Assert.False(item.IsOffscreen);
|
||||
Assert.Contains(label, item.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>About: shows the build version.</summary>
|
||||
[Theory]
|
||||
[InlineData(SampleKind.WinUI)]
|
||||
|
||||
Reference in new issue
Block a user