|
|
@@ -39,8 +39,8 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
private var downloadButton: NSButton!
|
|
|
private var moreButton: NSButton!
|
|
|
private var siteNameLabel: NSTextField!
|
|
|
- private var siteSubtitleLabel: NSTextField!
|
|
|
private var siteIconView: NSImageView!
|
|
|
+ private var currentSiteIconURLKey: String?
|
|
|
private var isPinnedOnTop = false
|
|
|
private var isGestureModeEnabled = true
|
|
|
|
|
|
@@ -146,13 +146,16 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
action: #selector(showLauncher)
|
|
|
)
|
|
|
|
|
|
- // Center capsule (icon + site + optional page title).
|
|
|
+ // Center site identity (logo + app/site name).
|
|
|
siteIconView = NSImageView()
|
|
|
siteIconView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
|
|
|
.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
|
|
|
siteIconView.contentTintColor = NSColor.secondaryLabelColor
|
|
|
siteIconView.imageScaling = .scaleProportionallyDown
|
|
|
+ siteIconView.wantsLayer = true
|
|
|
+ siteIconView.layer?.cornerRadius = 3
|
|
|
+ siteIconView.layer?.masksToBounds = true
|
|
|
|
|
|
siteNameLabel = NSTextField(labelWithString: windowTitle ?? "Browser")
|
|
|
siteNameLabel.font = NSFont.systemFont(ofSize: 13, weight: .semibold)
|
|
|
@@ -160,23 +163,15 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
siteNameLabel.alignment = .left
|
|
|
siteNameLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
|
|
- siteSubtitleLabel = NSTextField(labelWithString: "")
|
|
|
- siteSubtitleLabel.font = NSFont.systemFont(ofSize: 10.5, weight: .medium)
|
|
|
- siteSubtitleLabel.textColor = NSColor.secondaryLabelColor
|
|
|
- siteSubtitleLabel.alignment = .left
|
|
|
- siteSubtitleLabel.lineBreakMode = .byTruncatingTail
|
|
|
-
|
|
|
- let textStack = NSStackView(views: [siteNameLabel, siteSubtitleLabel])
|
|
|
- textStack.orientation = .vertical
|
|
|
- textStack.alignment = .leading
|
|
|
- textStack.spacing = 0
|
|
|
- textStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
-
|
|
|
- let headerStack = NSStackView(views: [siteIconView, textStack])
|
|
|
+ let headerStack = NSStackView(views: [siteIconView, siteNameLabel])
|
|
|
headerStack.orientation = .horizontal
|
|
|
headerStack.alignment = .centerY
|
|
|
headerStack.spacing = 8
|
|
|
headerStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
+ siteIconView.widthAnchor.constraint(equalToConstant: 14),
|
|
|
+ siteIconView.heightAnchor.constraint(equalToConstant: 14),
|
|
|
+ ])
|
|
|
|
|
|
// Open in external browser
|
|
|
openInBrowserButton = makeIconButton(
|
|
|
@@ -256,28 +251,7 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
private func configureTitlebarLayout(centerView: NSView) {
|
|
|
guard let window else { return }
|
|
|
|
|
|
- let centerCapsule = NSVisualEffectView()
|
|
|
- centerCapsule.material = .titlebar
|
|
|
- centerCapsule.blendingMode = .withinWindow
|
|
|
- centerCapsule.state = .active
|
|
|
- centerCapsule.wantsLayer = true
|
|
|
- centerCapsule.layer?.cornerRadius = 12
|
|
|
- centerCapsule.layer?.masksToBounds = true
|
|
|
- centerCapsule.layer?.borderWidth = 1
|
|
|
- centerCapsule.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.6).cgColor
|
|
|
- centerCapsule.translatesAutoresizingMaskIntoConstraints = false
|
|
|
-
|
|
|
- centerCapsule.addSubview(centerView)
|
|
|
centerView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- NSLayoutConstraint.activate([
|
|
|
- centerView.leadingAnchor.constraint(equalTo: centerCapsule.leadingAnchor, constant: 12),
|
|
|
- centerView.trailingAnchor.constraint(equalTo: centerCapsule.trailingAnchor, constant: -12),
|
|
|
- centerView.topAnchor.constraint(equalTo: centerCapsule.topAnchor, constant: 5),
|
|
|
- centerView.bottomAnchor.constraint(equalTo: centerCapsule.bottomAnchor, constant: -5),
|
|
|
- centerCapsule.heightAnchor.constraint(equalToConstant: 30),
|
|
|
- centerCapsule.widthAnchor.constraint(greaterThanOrEqualToConstant: 240),
|
|
|
- centerCapsule.widthAnchor.constraint(lessThanOrEqualToConstant: 520),
|
|
|
- ])
|
|
|
|
|
|
let leftStack = NSStackView(views: [backButton, forwardButton, reloadButton, homeButton])
|
|
|
leftStack.orientation = .horizontal
|
|
|
@@ -314,7 +288,7 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
|
|
|
let centerContainer = NSView()
|
|
|
centerContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- centerContainer.addSubview(centerCapsule)
|
|
|
+ centerContainer.addSubview(centerView)
|
|
|
|
|
|
let rightContainer = NSView()
|
|
|
rightContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -340,13 +314,17 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
leftContainer.widthAnchor.constraint(equalTo: rightContainer.widthAnchor),
|
|
|
|
|
|
leftStack.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor),
|
|
|
+ leftStack.trailingAnchor.constraint(equalTo: leftContainer.trailingAnchor),
|
|
|
leftStack.centerYAnchor.constraint(equalTo: leftContainer.centerYAnchor),
|
|
|
|
|
|
+ rightStack.leadingAnchor.constraint(equalTo: rightContainer.leadingAnchor),
|
|
|
rightStack.trailingAnchor.constraint(equalTo: rightContainer.trailingAnchor),
|
|
|
rightStack.centerYAnchor.constraint(equalTo: rightContainer.centerYAnchor),
|
|
|
|
|
|
- centerCapsule.centerXAnchor.constraint(equalTo: centerContainer.centerXAnchor),
|
|
|
- centerCapsule.centerYAnchor.constraint(equalTo: centerContainer.centerYAnchor),
|
|
|
+ centerView.centerXAnchor.constraint(equalTo: centerContainer.centerXAnchor),
|
|
|
+ centerView.centerYAnchor.constraint(equalTo: centerContainer.centerYAnchor),
|
|
|
+ centerView.leadingAnchor.constraint(greaterThanOrEqualTo: centerContainer.leadingAnchor, constant: 6),
|
|
|
+ centerView.trailingAnchor.constraint(lessThanOrEqualTo: centerContainer.trailingAnchor, constant: -6),
|
|
|
])
|
|
|
|
|
|
window.addTitlebarAccessoryViewController(accessory(topBar, layout: .top))
|
|
|
@@ -480,9 +458,32 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
let preferred = (host?.isEmpty == false) ? host! : ((pageTitle?.isEmpty == false) ? pageTitle! : "Browser")
|
|
|
|
|
|
siteNameLabel.stringValue = preferred
|
|
|
- siteSubtitleLabel.stringValue = (pageTitle?.isEmpty == false && pageTitle != preferred) ? pageTitle! : ""
|
|
|
- siteSubtitleLabel.isHidden = siteSubtitleLabel.stringValue.isEmpty
|
|
|
window?.title = preferred
|
|
|
+
|
|
|
+ guard let url = webView.url else {
|
|
|
+ siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
|
|
|
+ .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let key = (url.host ?? url.absoluteString).lowercased()
|
|
|
+ guard currentSiteIconURLKey != key else { return }
|
|
|
+ currentSiteIconURLKey = key
|
|
|
+
|
|
|
+ Task { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ let fetched = await FaviconImageCache.shared.image(for: url)
|
|
|
+ await MainActor.run {
|
|
|
+ guard self.currentSiteIconURLKey == key else { return }
|
|
|
+ if let fetched {
|
|
|
+ self.siteIconView.image = fetched
|
|
|
+ } else {
|
|
|
+ self.siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
|
|
|
+ .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
|
|
|
+ self.siteIconView.contentTintColor = NSColor.secondaryLabelColor
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private func updateToolbarState() {
|