Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Mac/AppDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ enum FontSize: Int {
case veryLarge = 3
}

enum ArticleThemeSelectionMode: Int, CaseIterable, Sendable {
case single = 0
case appearance = 1

var title: String {
switch self {
case .single:
return NSLocalizedString("Same Theme for All Appearances", comment: "Article theme selection mode")
case .appearance:
return NSLocalizedString("Match Appearance", comment: "Article theme selection mode")
}
}
}

final class AppDefaults: Sendable {

static let defaultThemeName = "Default"
Expand Down Expand Up @@ -42,6 +56,9 @@ final class AppDefaults: Sendable {
static let exportOPMLAccountID = "exportOPMLAccountID"
static let defaultBrowserID = "defaultBrowserID"
static let currentThemeName = "currentThemeName"
static let articleThemeSelectionMode = "articleThemeSelectionMode"
static let lightThemeName = "lightThemeName"
static let darkThemeName = "darkThemeName"
static let articleContentJavascriptEnabled = "articleContentJavascriptEnabled"

// Hidden prefs
Expand Down Expand Up @@ -221,6 +238,33 @@ final class AppDefaults: Sendable {
}
}

var articleThemeSelectionMode: ArticleThemeSelectionMode {
get {
ArticleThemeSelectionMode(rawValue: AppDefaults.int(for: Key.articleThemeSelectionMode)) ?? .single
}
set {
AppDefaults.setInt(for: Key.articleThemeSelectionMode, newValue.rawValue)
}
}

var lightThemeName: String? {
get {
return AppDefaults.string(for: Key.lightThemeName)
}
set {
AppDefaults.setString(for: Key.lightThemeName, newValue)
}
}

var darkThemeName: String? {
get {
return AppDefaults.string(for: Key.darkThemeName)
}
set {
AppDefaults.setString(for: Key.darkThemeName, newValue)
}
}

var showTitleOnMainWindow: Bool {
return AppDefaults.bool(for: Key.showTitleOnMainWindow)
}
Expand Down Expand Up @@ -344,6 +388,7 @@ final class AppDefaults: Sendable {
Key.refreshInterval: RefreshInterval.every2Hours.rawValue,
Key.showDebugMenu: showDebugMenu,
Key.currentThemeName: Self.defaultThemeName,
Key.articleThemeSelectionMode: ArticleThemeSelectionMode.single.rawValue,
Key.articleContentJavascriptEnabled: true
]

Expand Down
1 change: 1 addition & 0 deletions Mac/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ let appName = "NetNewsWire"
}

ArticleThemesManager.shared.start()
ArticleThemesManager.shared.updateCurrentAppearance(NSApplication.shared.effectiveAppearance.isDarkMode ? .dark : .light)
NetworkMonitor.shared.start()
MemoryPressureMonitor.shared.start()

Expand Down
6 changes: 6 additions & 0 deletions Mac/MainWindow/Detail/DetailWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ final class DetailWebView: WKWebView {
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
updateObscuredContentInsets()
ArticleThemesManager.shared.updateCurrentAppearance(NSApplication.shared.effectiveAppearance.isDarkMode ? .dark : .light)

if let window, !isObservingResizeNotifications {
NotificationCenter.default.addObserver(
Expand All @@ -70,6 +71,11 @@ final class DetailWebView: WKWebView {
}
}

override func viewDidChangeEffectiveAppearance() {
super.viewDidChangeEffectiveAppearance()
ArticleThemesManager.shared.updateCurrentAppearance(NSApplication.shared.effectiveAppearance.isDarkMode ? .dark : .light)
}

@objc func windowDidResize(_ notification: Notification) {
updateObscuredContentInsets()
}
Expand Down
6 changes: 3 additions & 3 deletions Mac/MainWindow/MainWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ final class MainWindowController: NSWindowController, NSUserInterfaceValidations
}

@objc func selectArticleTheme(_ menuItem: NSMenuItem) {
ArticleThemesManager.shared.currentThemeName = menuItem.title
ArticleThemesManager.shared.setActiveAppearanceThemeName(menuItem.title)
}
}

Expand Down Expand Up @@ -1452,7 +1452,7 @@ private extension MainWindowController {
let defaultThemeItem = NSMenuItem()
defaultThemeItem.title = ArticleTheme.defaultTheme.name
defaultThemeItem.action = #selector(selectArticleTheme(_:))
defaultThemeItem.state = defaultThemeItem.title == ArticleThemesManager.shared.currentThemeName ? .on : .off
defaultThemeItem.state = defaultThemeItem.title == ArticleThemesManager.shared.activeThemeName ? .on : .off
articleThemeMenu.addItem(defaultThemeItem)

articleThemeMenu.addItem(NSMenuItem.separator())
Expand All @@ -1461,7 +1461,7 @@ private extension MainWindowController {
let themeItem = NSMenuItem()
themeItem.title = themeName
themeItem.action = #selector(selectArticleTheme(_:))
themeItem.state = themeItem.title == ArticleThemesManager.shared.currentThemeName ? .on : .off
themeItem.state = themeItem.title == ArticleThemesManager.shared.activeThemeName ? .on : .off
articleThemeMenu.addItem(themeItem)
}

Expand Down
75 changes: 68 additions & 7 deletions Mac/Preferences/General/GeneralPrefencesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ final class GeneralPreferencesViewController: NSViewController {
@IBOutlet var articleThemePopup: NSPopUpButton!
@IBOutlet var defaultBrowserPopup: NSPopUpButton!

private enum ArticleThemePopupItem {
case selectionMode(ArticleThemeSelectionMode)
case theme(ArticleThemeSetting)
}

public override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
commonInit()
Expand Down Expand Up @@ -56,7 +61,7 @@ final class GeneralPreferencesViewController: NSViewController {
guard let menuItem = articleThemePopup.selectedItem else {
return
}
ArticleThemesManager.shared.currentThemeName = menuItem.title
applyArticleThemePopupItem(menuItem)
updateArticleThemePopup()
}

Expand Down Expand Up @@ -121,19 +126,75 @@ private extension GeneralPreferencesViewController {
let menu = articleThemePopup.menu!
menu.removeAllItems()

menu.addItem(NSMenuItem(title: ArticleTheme.defaultTheme.name, action: nil, keyEquivalent: ""))
let sameThemeItem = articleThemeMenuItem(title: ArticleThemeSelectionMode.single.title, representedObject: ArticleThemePopupItem.selectionMode(.single))
sameThemeItem.state = ArticleThemesManager.shared.themeSelectionMode == .single ? .on : .off
menu.addItem(sameThemeItem)

let matchAppearanceItem = articleThemeMenuItem(title: ArticleThemeSelectionMode.appearance.title, representedObject: ArticleThemePopupItem.selectionMode(.appearance))
matchAppearanceItem.state = ArticleThemesManager.shared.themeSelectionMode == .appearance ? .on : .off
menu.addItem(matchAppearanceItem)
menu.addItem(NSMenuItem.separator())

for themeName in ArticleThemesManager.shared.themeNames {
menu.addItem(NSMenuItem(title: themeName, action: nil, keyEquivalent: ""))
switch ArticleThemesManager.shared.themeSelectionMode {
case .single:
addThemeMenuItems(to: menu, setting: .single)
articleThemePopup.selectItem(withTitle: ArticleThemesManager.shared.currentThemeName)
if articleThemePopup.indexOfSelectedItem == -1 {
articleThemePopup.selectItem(withTitle: ArticleTheme.defaultTheme.name)
}
case .appearance:
addThemeSubmenu(to: menu, setting: .lightAppearance)
addThemeSubmenu(to: menu, setting: .darkAppearance)
articleThemePopup.selectItem(withTitle: ArticleThemeSelectionMode.appearance.title)
}
}

@objc func articleThemeMenuItemSelected(_ menuItem: NSMenuItem) {
applyArticleThemePopupItem(menuItem)
updateArticleThemePopup()
}

func applyArticleThemePopupItem(_ menuItem: NSMenuItem) {
guard let item = menuItem.representedObject as? ArticleThemePopupItem else {
return
}

articleThemePopup.selectItem(withTitle: ArticleThemesManager.shared.currentThemeName)
if articleThemePopup.indexOfSelectedItem == -1 {
articleThemePopup.selectItem(withTitle: ArticleTheme.defaultTheme.name)
switch item {
case .selectionMode(let mode):
ArticleThemesManager.shared.themeSelectionMode = mode
case .theme(let setting):
ArticleThemesManager.shared.setThemeName(menuItem.title, for: setting)
}
}

func addThemeSubmenu(to menu: NSMenu, setting: ArticleThemeSetting) {
let submenuItem = NSMenuItem(title: setting.title, action: nil, keyEquivalent: "")
let submenu = NSMenu()
addThemeMenuItems(to: submenu, setting: setting)
submenuItem.submenu = submenu
menu.addItem(submenuItem)
}

func addThemeMenuItems(to menu: NSMenu, setting: ArticleThemeSetting) {
let defaultThemeItem = articleThemeMenuItem(title: ArticleTheme.defaultTheme.name, representedObject: ArticleThemePopupItem.theme(setting))
defaultThemeItem.state = ArticleTheme.defaultTheme.name == ArticleThemesManager.shared.themeName(for: setting) ? .on : .off
menu.addItem(defaultThemeItem)
menu.addItem(NSMenuItem.separator())

for themeName in ArticleThemesManager.shared.themeNames {
let themeItem = articleThemeMenuItem(title: themeName, representedObject: ArticleThemePopupItem.theme(setting))
themeItem.state = themeName == ArticleThemesManager.shared.themeName(for: setting) ? .on : .off
menu.addItem(themeItem)
}
}

private func articleThemeMenuItem(title: String, representedObject: ArticleThemePopupItem) -> NSMenuItem {
let menuItem = NSMenuItem(title: title, action: #selector(articleThemeMenuItemSelected(_:)), keyEquivalent: "")
menuItem.target = self
menuItem.representedObject = representedObject
return menuItem
}

func updateBrowserPopup() {
let menu = defaultBrowserPopup.menu!
let allBrowsers = MacWebBrowser.sortedBrowsers()
Expand Down
Loading