diff --git a/Mac/About/AboutWindowController.swift b/Mac/About/AboutWindowController.swift new file mode 100644 index 000000000..942ba327e --- /dev/null +++ b/Mac/About/AboutWindowController.swift @@ -0,0 +1,80 @@ +// +// AboutWindowController.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 26/06/2025. +// Copyright © 2025 Ranchero Software. All rights reserved. +// + +import Cocoa + +class AboutWindowController: NSWindowController { + + @IBOutlet weak var appIconImageView: NSImageView! + @IBOutlet weak var appTitleLabel: NSTextField! + @IBOutlet weak var versionLabel: NSTextField! + @IBOutlet weak var copyrightLabel: NSTextField! + @IBOutlet weak var websiteLabel: LinkLabel! + @IBOutlet weak var creditsTextView: NSTextView! + + override func windowDidLoad() { + super.windowDidLoad() + configureWindow() + updateUI() + } + + func configureWindow() { + window?.isOpaque = false + window?.backgroundColor = .clear + window?.titlebarAppearsTransparent = true + window?.titleVisibility = .hidden + window?.styleMask.insert(.fullSizeContentView) + if let contentView = window?.contentView { + let visualEffectView = NSVisualEffectView(frame: contentView.bounds) + visualEffectView.autoresizingMask = [.width, .height] + visualEffectView.blendingMode = .behindWindow + visualEffectView.material = .titlebar + visualEffectView.state = .active + contentView.addSubview(visualEffectView, positioned: .below, relativeTo: nil) + } + } + + private func updateUI() { + + // App Icon + appIconImageView.image = NSApp.applicationIconImage + + // Version + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" + let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown" + let versionString = "Version \(version) (Build \(build))" + versionLabel.stringValue = versionString + + // Copyright + let copyright = Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String ?? "Copyright © 2002-2025 Brent Simmons. All rights reserved." + copyrightLabel.stringValue = copyright + + // Credits + if let creditsURL = Bundle.main.url(forResource: "Credits", withExtension: "rtf"), + let creditsData = try? Data(contentsOf: creditsURL), + let attributedString = try? NSAttributedString(data: creditsData, options: [.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil) { + creditsTextView.textStorage?.setAttributedString(attributedString) + } else { + creditsTextView.string = "Credits not available." + } + + let fullRange = NSRange(location: 0, length: creditsTextView.string.utf16.count) + let leadingParagraphStyle = NSMutableParagraphStyle() + leadingParagraphStyle.alignment = .left + creditsTextView.textStorage?.addAttribute(.paragraphStyle, value: leadingParagraphStyle, range: fullRange) + + // URL + let url = URL(string: "https://inessential.com")! + let attributedString = NSMutableAttributedString(string: "inessential.com") + attributedString.addAttribute(.link, value: url, range: NSRange(location: 0, length: attributedString.length)) + attributedString.addAttribute(.foregroundColor, value: NSColor.systemBlue, range: NSRange(location: 0, length: attributedString.length)) + attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: attributedString.length)) + websiteLabel.attributedStringValue = attributedString + } + +} diff --git a/Mac/About/AboutWindowController.xib b/Mac/About/AboutWindowController.xib new file mode 100644 index 000000000..52857ec84 --- /dev/null +++ b/Mac/About/AboutWindowController.xib @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac/About/LinkLabel.swift b/Mac/About/LinkLabel.swift new file mode 100644 index 000000000..4f298175b --- /dev/null +++ b/Mac/About/LinkLabel.swift @@ -0,0 +1,16 @@ +// +// LinkLabel.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 26/06/2025. +// Copyright © 2025 Ranchero Software. All rights reserved. +// + + +class LinkLabel: NSTextField { + + /// pièces de résistance -- keeping it a mac-assed mac app. + override func resetCursorRects() { + addCursorRect(bounds, cursor: .pointingHand) + } +} diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index adf91c8a5..1ff0799a5 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -277,7 +277,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, CrashReporter.check(crashReporter: self.crashReporter) } #endif - } func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void) -> Bool { @@ -675,6 +674,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, createAndShowMainWindowIfNecessary() mainWindowController!.gotoStarred(sender) } + + @IBAction func showCustomAboutPanel(_ sender: Any?) { + let aboutWC = AboutWindowController(windowNibName: "AboutWindowController") + aboutWC.showWindow(nil) + aboutWC.window?.center() + aboutWC.window?.makeKeyAndOrderFront(nil) + } @IBAction func sortByOldestArticleOnTop(_ sender: Any?) { diff --git a/Mac/Base.lproj/Main.storyboard b/Mac/Base.lproj/Main.storyboard index c2906ddec..536c5fe1c 100644 --- a/Mac/Base.lproj/Main.storyboard +++ b/Mac/Base.lproj/Main.storyboard @@ -1,8 +1,7 @@ - + - - + @@ -18,7 +17,7 @@ - + diff --git a/Mac/Resources/Credits.rtf b/Mac/Resources/Credits.rtf index ce67ed372..1291f7c3a 100644 --- a/Mac/Resources/Credits.rtf +++ b/Mac/Resources/Credits.rtf @@ -1,52 +1,64 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2761 -\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;\f1\fnil\fcharset0 LucidaGrande;} +{\rtf1\ansi\ansicpg1252\cocoartf2859 +\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 SFPro-Bold;\f1\fnil\fcharset0 SFPro-Regular;} {\colortbl;\red255\green255\blue255;\red0\green0\blue0;} {\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;} \vieww14060\viewh15660\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\sl360\slmult1\pardirnatural\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "https://ranchero.com/netnewswire/"}}{\fldrslt -\f0\b\fs22 \cf2 NetNewsWire}} -\f0\b\fs22 \cf2 \ -By Brent Simmons\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\sl360\slmult1\pardirnatural\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "http://inessential.com/"}}{\fldrslt \cf2 inessential.com}} +\deftab720 +\pard\pardeftab720\sa60\partightenfactor0 + +\f0\b\fs22 \cf2 Credits \f1\b0 \ +\pard\pardeftab720\sl144\slmult1\sa60\partightenfactor0 +\cf2 \ +\pard\pardeftab720\sl168\slmult1\sa60\partightenfactor0 +\cf2 Contributing Developers \ +{\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}, {\field{\*\fldinst{HYPERLINK "https://stuartbreckenridge.net/"}}{\fldrslt Stuart Breckenridge}}\ +\ +App Icon\ +{\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\ +\ +Feedly Syncing\ +{\field{\*\fldinst{HYPERLINK "https://twitter.com/kielgillard"}}{\fldrslt Kiel Gillard}}\ +\ +NewsBlur Syncing\ +{\field{\*\fldinst{HYPERLINK "https://twitter.com/quanganhdo"}}{\fldrslt Anh Do}}\ +\ +Under-the-Hood Magic and CSS Stylin's\ +{\field{\*\fldinst{HYPERLINK "https://github.com/wevah"}}{\fldrslt Nate Weaver}}\ +\ +Newsfoot (JS footnote displayer)\ +{\field{\*\fldinst{HYPERLINK "https://github.com/brehaut/"}}{\fldrslt Andrew Brehaut}}\ +\ +Help Book\ +{\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\ +\ +And featuring contributions from\ +{\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut}}, {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"}}{\fldrslt Joe Heck}}, {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://blog.rizwan.dev/"}}{\fldrslt Rizwan Mohamed Ibrahim}}, {\field{\*\fldinst{HYPERLINK "https://twitter.com/philviso"}}{\fldrslt Phil Viso}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors"}}{\fldrslt many more}}!\ \pard\pardeftab720\li360\sa60\partightenfactor0 \cf2 \ \pard\pardeftab720\sa60\partightenfactor0 -\f0\b \cf2 Credits: -\f1\b0 \ -\pard\pardeftab720\li360\sa60\partightenfactor0 -\cf2 Contributing developers: {\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}, {\field{\*\fldinst{HYPERLINK "https://stuartbreckenridge.net/"}}{\fldrslt Stuart Breckenridge}}\ -App icon: {\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\ -Feedly syncing: {\field{\*\fldinst{HYPERLINK "https://twitter.com/kielgillard"}}{\fldrslt Kiel Gillard}}\ -NewsBlur syncing: {\field{\*\fldinst{HYPERLINK "https://twitter.com/quanganhdo"}}{\fldrslt Anh Do}}\ -Under-the-hood magic and CSS stylin\'92s: {\field{\*\fldinst{HYPERLINK "https://github.com/wevah"}}{\fldrslt Nate Weaver}}\ -Newsfoot (JS footnote displayer): {\field{\*\fldinst{HYPERLINK "https://github.com/brehaut/"}}{\fldrslt Andrew Brehaut}}\ -Help book: {\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\ -And featuring contributions from {\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut}}, {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"}}{\fldrslt Joe Heck}}, {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://blog.rizwan.dev/"}}{\fldrslt Rizwan Mohamed Ibrahim}}, {\field{\*\fldinst{HYPERLINK "https://twitter.com/philviso"}}{\fldrslt Phil Viso}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors"}}{\fldrslt many more}}!\ -\ -\pard\pardeftab720\sa60\partightenfactor0 +\f0\b \cf2 Acknowledgments\ -\f0\b \cf2 Acknowledgments: \f1\b0 \ -\pard\pardeftab720\li360\sa60\partightenfactor0 +\pard\pardeftab720\sl168\slmult1\sa60\partightenfactor0 {\field{\*\fldinst{HYPERLINK "https://github.com/ccgus/fmdb"}}{\fldrslt \cf2 FMDB}} (greatest SQLite wrapper ever in history) is by {\field{\*\fldinst{HYPERLINK "http://flyingmeat.com/"}}{\fldrslt Flying Meat Software}}.\ -\pard\pardeftab720\li360\sa60\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "https://sparkle-project.org/"}}{\fldrslt \cf2 Sparkle}} is by Sparkle Project.\ \ +{\field{\*\fldinst{HYPERLINK "https://sparkle-project.org/"}}{\fldrslt Sparkle}} is by Sparkle Project.\ +\pard\pardeftab720\li360\sa60\partightenfactor0 +\cf2 \ \pard\pardeftab720\sa60\partightenfactor0 -\f0\b \cf2 Thanks:\ -\pard\pardeftab720\li360\sa60\partightenfactor0 - -\f1\b0 \cf2 Thanks to Sheila and my family; thanks to my friends in Seattle and around the globe; thanks to the ever-patient and ever-awesome NetNewsWire beta testers. Thanks to {\field{\*\fldinst{HYPERLINK "https://github.com/"}}{\fldrslt GitHub}} and {\field{\*\fldinst{HYPERLINK "https://slack.com/"}}{\fldrslt Slack}} for making open source collaboration easy and fun.\ +\f0\b \cf2 Thanks\ \ + +\f1\b0 Thanks to Sheila and my family; thanks to my friends in Seattle and around the globe; thanks to the ever-patient and ever-awesome NetNewsWire beta testers. Thanks to {\field{\*\fldinst{HYPERLINK "https://github.com/"}}{\fldrslt GitHub}} and {\field{\*\fldinst{HYPERLINK "https://slack.com/"}}{\fldrslt Slack}} for making open source collaboration easy and fun.\ +\pard\pardeftab720\li360\sa60\partightenfactor0 +\cf2 \ \pard\pardeftab720\sa60\partightenfactor0 -\f0\b \cf2 Dedication:\ -\pard\pardeftab720\li360\sa60\partightenfactor0 +\f0\b \cf2 Dedication\ +\ -\f1\b0 \cf2 NetNewsWire 6 is dedicated to everyone working to save democracy around the world.\ +\f1\b0 NetNewsWire 6 is dedicated to everyone working to save democracy around the world.\ } \ No newline at end of file