From 947fbaddfaac8dddb2d15aff5907ac52a8702cef Mon Sep 17 00:00:00 2001 From: David Richardson Date: Fri, 23 Oct 2026 16:45:51 -0600 Subject: [PATCH] AppKit: Show CPBundleDisplayName as the application's name AppKit read only CPBundleName for the name that users see, so an application's display name was ignored. It now follows Cocoa, which shows CFBundleDisplayName and falls back to CFBundleName. * CPApplication.j: Add _applicationName, which returns CPBundleDisplayName or, without one, CPBundleName, and use it as the About panel title. * CPMenu/_CPMenuBarWindow.j: Use the display name in the browser tab title. * Tests/Manual/TextEditor/Info.plist: Rename the display name key from CFBundleDisplayName to CPBundleDisplayName. * Tests/Manual/TextEditor/WORKLIST.md: Remove the entry for this gap, and record the About panel as tested. --- AppKit/CPApplication.j | 16 ++++++++++++++-- AppKit/CPMenu/_CPMenuBarWindow.j | 2 +- Tests/Manual/TextEditor/Info.plist | 2 +- Tests/Manual/TextEditor/WORKLIST.md | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 5d0a3b911..0e81be5b2 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -375,7 +375,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0, to the following respective keys in your info.plist file.
-    CPBundleName
+    CPBundleDisplayName, or CPBundleName when there is no display name
     CPApplicationIcon (through a call to -applicationIconImage, see documentation for that method for more details)
     CPBundleVersion
     CPBundleShortVersionString
@@ -389,7 +389,7 @@ var CPApplicationDelegate_applicationShouldTerminate_           = 1 << 0,
     if (!_aboutPanel)
     {
         var mainInfo = [[CPBundle mainBundle] infoDictionary],
-            applicationTitle = [options objectForKey:"ApplicationName"] || [mainInfo objectForKey:@"CPBundleName"],
+            applicationTitle = [options objectForKey:"ApplicationName"] || [self _applicationName],
             applicationIcon = [options objectForKey:@"ApplicationIcon"] || [self applicationIconImage],
             version = [options objectForKey:@"Version"] || [mainInfo objectForKey:@"CPBundleVersion"],
             applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
@@ -793,6 +793,18 @@ var CPApplicationDelegate_applicationShouldTerminate_           = 1 << 0,
         [aMenu _setMenuName:@"CPMainMenu"];
 }
 
+/*
+    @ignore
+    Returns the application's name as users see it: CPBundleDisplayName from the main bundle's
+    Info.plist, or CPBundleName when there is no display name.
+*/
+- (CPString)_applicationName
+{
+    var mainBundle = [CPBundle mainBundle];
+
+    return [mainBundle objectForInfoDictionaryKey:@"CPBundleDisplayName"] || [mainBundle objectForInfoDictionaryKey:@"CPBundleName"];
+}
+
 // MARK: Managing the Windows Menu
 
 /*!
diff --git a/AppKit/CPMenu/_CPMenuBarWindow.j b/AppKit/CPMenu/_CPMenuBarWindow.j
index 7b0b0d0bb..4a40d0a75 100644
--- a/AppKit/CPMenu/_CPMenuBarWindow.j
+++ b/AppKit/CPMenu/_CPMenuBarWindow.j
@@ -103,7 +103,7 @@
 {
     if (CPDOMAvailable)
     {
-        var bundleName = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleName"];
+        var bundleName = [CPApp _applicationName];
 
         if (![bundleName length])
             document.title = aTitle;
diff --git a/Tests/Manual/TextEditor/Info.plist b/Tests/Manual/TextEditor/Info.plist
index a24324e2a..290c9e51e 100644
--- a/Tests/Manual/TextEditor/Info.plist
+++ b/Tests/Manual/TextEditor/Info.plist
@@ -23,7 +23,7 @@
 	1.0
 	CPHumanReadableCopyright
 	Copyright © 2026, David Richardson, MIT Licensed
-	CFBundleDisplayName
+	CPBundleDisplayName
 	TextEdit by Cappuccino
 
 
diff --git a/Tests/Manual/TextEditor/WORKLIST.md b/Tests/Manual/TextEditor/WORKLIST.md
index 8dae2210e..5ae62e177 100644
--- a/Tests/Manual/TextEditor/WORKLIST.md
+++ b/Tests/Manual/TextEditor/WORKLIST.md
@@ -6,7 +6,6 @@ TextEditor is a document-based test application and a tutorial. When the app use
 
 ### Windows and documents
 
-- **AppKit ignores `CPBundleDisplayName`.** Cocoa uses `CFBundleDisplayName` for the name that the user sees, and `CFBundleName` as the short name. AppKit reads only `CPBundleName`: for the About panel title (`CPApplication.j:389`) and for the browser tab title (`_CPMenuBarWindow.j:106`). Both should use `CPBundleDisplayName`, with `CPBundleName` as the fallback.
 - **The "save changes" alert is modal for the whole app.** `CPDocument` shows it with `runModal` (`CPDocument.j:903`). Cocoa attaches it as a sheet to the document window, so that only that document is blocked.
 - **Save cannot work without a server.** With no file URL, Save uses `CPSavePanel`, which falls back to `window.prompt` (`CPSavePanel.j:79`). `CPDocument` then sends an HTTP POST. A storage facade is needed, with local (IndexedDB) and remote (HTTP) implementations.
 
@@ -70,4 +69,5 @@ TextEditor is a document-based test application and a tutorial. When the app use
 - A minimum size for document windows, through the window delegate. The window stops at the minimum while the pointer keeps moving, as in macOS, and it can still be moved and resized afterwards.
 - View > Show Toolbar and Hide Toolbar: the item's title follows the toolbar's visibility.
 - The Window menu lists the open document windows, marks the main window with a check mark and brings a window to the front when chosen. Bring All to Front can be tested once documents open in separate browser windows.
+- The About panel shows the display name from `CPBundleDisplayName`.
 - Builds with both the legacy toolchain and capp-build.