diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml
index d323bd5..0f4df29 100644
--- a/.github/workflows/build-native.yml
+++ b/.github/workflows/build-native.yml
@@ -34,7 +34,7 @@ concurrency:
jobs:
native:
- uses: SimonCropp/PlatformHeads/.github/workflows/build-native.yml@0.11.0
+ uses: SimonCropp/PlatformHeads/.github/workflows/build-native.yml@0.12.0
permissions:
contents: write
pull-requests: write
diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt
index 62c3263..356df52 100644
--- a/native/CMakeLists.txt
+++ b/native/CMakeLists.txt
@@ -14,7 +14,7 @@ include(FetchContent)
FetchContent_Declare(
platformheads
GIT_REPOSITORY https://github.com/SimonCropp/PlatformHeads.git
- GIT_TAG 0.11.0
+ GIT_TAG 0.12.0
GIT_SHALLOW ON)
FetchContent_MakeAvailable(platformheads)
include(${platformheads_SOURCE_DIR}/cmake/PlatformHeads.cmake)
diff --git a/native/swift/Package.swift b/native/swift/Package.swift
index 9285fc8..bf7c229 100644
--- a/native/swift/Package.swift
+++ b/native/swift/Package.swift
@@ -12,7 +12,7 @@ let package = Package(
dependencies: [
// What every AppKit head does underneath its drawing, shared with DiffEngine's viewer. The
// sixth pin of PlatformHeads, moved with the other five.
- .package(url: "https://github.com/SimonCropp/PlatformHeads", exact: "0.11.0")
+ .package(url: "https://github.com/SimonCropp/PlatformHeads", exact: "0.12.0")
],
targets: [
// Exists only to import the ABI. Swift does not guarantee struct layout, so the structs
diff --git a/src/BuildMonitor.Core/App/UpdateOutcome.cs b/src/BuildMonitor.Core/App/UpdateOutcome.cs
index 5b84c9f..14153bd 100644
--- a/src/BuildMonitor.Core/App/UpdateOutcome.cs
+++ b/src/BuildMonitor.Core/App/UpdateOutcome.cs
@@ -1,9 +1,9 @@
///
-/// How the last update went, for the tray it started again to report, worded from PlatformHeads'
-/// . The update runs in a shell nobody sees, after the tray has
-/// exited, so without this there is nothing to see at either end of it: the icon goes away and one
-/// comes back, and whether that is the new version or the old one after a failure looks exactly
-/// the same.
+/// How the last update went, for the tray it started again to report, through PlatformHeads'
+/// . The update runs in a shell nobody sees, after the tray
+/// has exited, so without this there is nothing to see at either end of it: the icon goes away and
+/// one comes back, and whether that is the new version or the old one after a failure looks
+/// exactly the same.
///
static class UpdateOutcome
{
@@ -13,41 +13,22 @@ static class UpdateOutcome
/// outcome is reported once rather than at every start, and the whole output goes to the log,
/// since a notification has room for a line of it.
///
- public static Notification? Take(string path)
- {
- ToolUpdateOutcome? outcome;
- try
- {
- outcome = ToolUpdateOutcome.Take(path);
- }
- catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
- {
- Log.Warning(exception, "Could not read the update outcome at {Path}", path);
- return null;
- }
-
- if (outcome is null)
- {
- return null;
- }
+ public static Notification? Take(string path) =>
+ ToolUpdateOutcome.Report(
+ path,
+ "BuildMonitor",
+ VersionReader.VersionString,
+ Taken,
+ _ => Log.Warning(_, "Could not read the update outcome at {Path}", path));
+ static void Taken(ToolUpdateOutcome outcome)
+ {
if (outcome.Succeeded)
{
Log.Information("Update finished. dotnet tool update said:\n{Output}", outcome.Output);
- return Describe();
+ return;
}
Log.Warning("Update failed, so {Version} is still the one installed. dotnet tool update said:\n{Output}", VersionReader.VersionString, outcome.Output);
- return Describe(outcome.Output);
}
-
- ///
- /// The version rather than what the tool said, because this is the tray the update started: it
- /// is the installed one, whether the update moved it or found it already current.
- ///
- public static Notification Describe() =>
- new("BuildMonitor updated", $"Now running {VersionReader.VersionString}.", Kind: NotificationKind.Info);
-
- public static Notification Describe(string output) =>
- new("Update failed", new ToolUpdateOutcome(false, output).Reason);
}
diff --git a/src/BuildMonitor.Native/Native/BmStructs.cs b/src/BuildMonitor.Native/Native/BmStructs.cs
index 4d6efd3..7962f56 100644
--- a/src/BuildMonitor.Native/Native/BmStructs.cs
+++ b/src/BuildMonitor.Native/Native/BmStructs.cs
@@ -231,7 +231,5 @@ static class BmFlags
public const int RowMember = 1 << 3;
public const int FieldEnabled = 1 << 0;
public const int ButtonEnabled = 1 << 0;
- public const int TrayEnabled = 1 << 0;
- public const int TraySeparator = 1 << 1;
public const int MenuSeparatorAbove = 1 << 0;
}
diff --git a/src/BuildMonitor.Native/Native/ScreenPayload.cs b/src/BuildMonitor.Native/Native/ScreenPayload.cs
index e93250d..768773e 100644
--- a/src/BuildMonitor.Native/Native/ScreenPayload.cs
+++ b/src/BuildMonitor.Native/Native/ScreenPayload.cs
@@ -266,7 +266,9 @@ public void Build(Screen source)
Id = Add(item.Id),
Label = Add(item.Label),
Icon = Add(item.IconName ?? ""),
- Flags = (item.Enabled ? BmFlags.TrayEnabled : 0) | (item.Separator ? BmFlags.TraySeparator : 0)
+ // PlatformHeads' TrayMenuFlags, whose first two are BM_TRAY_ENABLED and
+ // BM_TRAY_SEPARATOR. The ABI carries no checks, so the rest stay behind.
+ Flags = (int) (item.Flags & (TrayMenuFlags.Enabled | TrayMenuFlags.Separator))
});
}
}
diff --git a/src/BuildMonitor.Native/runtimes/osx-arm64/native/libbuildmonitor_ui.dylib b/src/BuildMonitor.Native/runtimes/osx-arm64/native/libbuildmonitor_ui.dylib
index 0e70c91..a4a3689 100644
Binary files a/src/BuildMonitor.Native/runtimes/osx-arm64/native/libbuildmonitor_ui.dylib and b/src/BuildMonitor.Native/runtimes/osx-arm64/native/libbuildmonitor_ui.dylib differ
diff --git a/src/BuildMonitor.Native/runtimes/osx-x64/native/libbuildmonitor_ui.dylib b/src/BuildMonitor.Native/runtimes/osx-x64/native/libbuildmonitor_ui.dylib
index 0e70c91..a4a3689 100644
Binary files a/src/BuildMonitor.Native/runtimes/osx-x64/native/libbuildmonitor_ui.dylib and b/src/BuildMonitor.Native/runtimes/osx-x64/native/libbuildmonitor_ui.dylib differ
diff --git a/src/BuildMonitor.Tests/UpdateOutcomeTests.cs b/src/BuildMonitor.Tests/UpdateOutcomeTests.cs
index d69489b..2653dde 100644
--- a/src/BuildMonitor.Tests/UpdateOutcomeTests.cs
+++ b/src/BuildMonitor.Tests/UpdateOutcomeTests.cs
@@ -21,7 +21,7 @@ public async Task TheFailureIsReportedOnceAndThenRemoved()
await File.WriteAllTextAsync(path, locked, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
var notification = UpdateOutcome.Take(path);
- await Assert.That(notification).IsEqualTo(new("Update failed", """Failed to uninstall tool package 'buildmonitor': Access to the path 'C:\Users\me\.dotnet\tools\.store\buildmonitor\0.1.0-beta.6' is denied."""));
+ await Assert.That(notification).IsEqualTo(new("BuildMonitor did not update", """Failed to uninstall tool package 'buildmonitor': Access to the path 'C:\Users\me\.dotnet\tools\.store\buildmonitor\0.1.0-beta.6' is denied."""));
await Assert.That(File.Exists(path)).IsFalse();
await Assert.That(UpdateOutcome.Take(path)).IsNull();
}
@@ -54,7 +54,7 @@ public async Task OutputWithNoStatusLineIsAFailure()
{
using var path = new TempFile();
await File.WriteAllTextAsync(path, "Tool 'buildmonitor' failed to update due to the following:\nIt did not work.\n");
- await Assert.That(UpdateOutcome.Take(path)).IsEqualTo(new("Update failed", "It did not work."));
+ await Assert.That(UpdateOutcome.Take(path)).IsEqualTo(new("BuildMonitor did not update", "It did not work."));
}
[Test]
@@ -66,11 +66,21 @@ public async Task NoFileMeansNothingToReport() =>
/// at the end are what the shells leave.
///
[Test]
- public async Task TheReasonIsTheLastLineWritten() =>
- await Assert.That(UpdateOutcome.Describe("Tool 'buildmonitor' failed to update due to the following:\r\nUnable to load the service index for source https://api.nuget.org/v3/index.json.\r\n\r\n").Message)
+ public async Task TheReasonIsTheLastLineWritten()
+ {
+ using var path = new TempFile();
+ await File.WriteAllTextAsync(path, "failed\r\nTool 'buildmonitor' failed to update due to the following:\r\nUnable to load the service index for source https://api.nuget.org/v3/index.json.\r\n\r\n");
+
+ await Assert.That(UpdateOutcome.Take(path)?.Message)
.IsEqualTo("Unable to load the service index for source https://api.nuget.org/v3/index.json.");
+ }
[Test]
- public async Task NoOutputStillSaysTheUpdateFailed() =>
- await Assert.That(UpdateOutcome.Describe(" \n")).IsEqualTo(new("Update failed", "dotnet tool update failed without saying why."));
+ public async Task NoOutputStillSaysTheUpdateFailed()
+ {
+ using var path = new TempFile();
+ await File.WriteAllTextAsync(path, "failed\n \n");
+
+ await Assert.That(UpdateOutcome.Take(path)).IsEqualTo(new("BuildMonitor did not update", "dotnet tool update failed without saying why."));
+ }
}
diff --git a/src/BuildMonitor.Windows/MenuColours.cs b/src/BuildMonitor.Windows/MenuColours.cs
deleted file mode 100644
index befea1b..0000000
--- a/src/BuildMonitor.Windows/MenuColours.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-///
-/// Dark menus. Assigned per strip rather than through ToolStripManager.Renderer, which is
-/// process wide state a test host would share.
-///
-sealed class MenuColours : ProfessionalColorTable
-{
- public override Color MenuItemSelected => Palette.SelectedRow;
- public override Color MenuItemSelectedGradientBegin => Palette.SelectedRow;
- public override Color MenuItemSelectedGradientEnd => Palette.SelectedRow;
- public override Color MenuItemPressedGradientBegin => Palette.SelectedRow;
- public override Color MenuItemPressedGradientEnd => Palette.SelectedRow;
- public override Color MenuItemBorder => Palette.Border;
- public override Color MenuBorder => Palette.Border;
- public override Color ToolStripDropDownBackground => Palette.Surface;
- public override Color ImageMarginGradientBegin => Palette.Surface;
- public override Color ImageMarginGradientMiddle => Palette.Surface;
- public override Color ImageMarginGradientEnd => Palette.Surface;
- public override Color SeparatorDark => Palette.Border;
- public override Color SeparatorLight => Palette.Border;
-}
diff --git a/src/BuildMonitor.Windows/MenuRenderer.cs b/src/BuildMonitor.Windows/MenuRenderer.cs
deleted file mode 100644
index c0eedbd..0000000
--- a/src/BuildMonitor.Windows/MenuRenderer.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-sealed class MenuRenderer() : ToolStripProfessionalRenderer(new MenuColours())
-{
- protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs args)
- {
- args.TextColor = args.Item.Enabled ? Palette.Text : Palette.Dim;
- base.OnRenderItemText(args);
- }
-
- protected override void OnRenderArrow(ToolStripArrowRenderEventArgs args)
- {
- args.ArrowColor = Palette.Text;
- base.OnRenderArrow(args);
- }
-}
diff --git a/src/BuildMonitor.Windows/MenuTheme.cs b/src/BuildMonitor.Windows/MenuTheme.cs
index 68b4db5..f05b36c 100644
--- a/src/BuildMonitor.Windows/MenuTheme.cs
+++ b/src/BuildMonitor.Windows/MenuTheme.cs
@@ -1,9 +1,25 @@
+///
+/// Dark or light menus, drawn by PlatformHeads' . Applied each
+/// time a menu opens, so the renderer is made from the palette as it is then, and a menu follows a
+/// change of theme as it did when the renderer read the palette itself.
+///
static class MenuTheme
{
public static void Apply(ToolStrip strip)
{
- strip.Renderer = new MenuRenderer();
+ strip.Renderer = new PaletteMenuRenderer(Colours());
strip.BackColor = Palette.Surface;
strip.ForeColor = Palette.Text;
}
+
+ static MenuPalette Colours() =>
+ new(
+ Background: Palette.Surface,
+ Border: Palette.Border,
+ ItemBorder: Palette.Border,
+ Selected: Palette.SelectedRow,
+ Pressed: Palette.SelectedRow,
+ Separator: Palette.Border,
+ Text: Palette.Text,
+ Dim: Palette.Dim);
}
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 77d17f5..222d675 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -14,10 +14,10 @@
-
-
-
-
+
+
+
+