| | |
| | | : .activationFailed |
| | | } |
| | | |
| | | func activate(window requestedWindow: AlignerWindow) throws -> WindowActivationResult { |
| | | let window = refreshedFinderWindowForPreciseAction(requestedWindow, operation: "windowActivation") |
| | | func activate(window: AlignerWindow) throws -> WindowActivationResult { |
| | | DevelopmentDiagnostics.log("windowActivation.activate.start", [ |
| | | "windowID": window.id, |
| | | "identifierSource": String(describing: window.identifierSource), |
| | |
| | | : .appActivatedOnly |
| | | } |
| | | |
| | | func close(window requestedWindow: AlignerWindow) throws -> WindowCloseResult { |
| | | let window = refreshedFinderWindowForPreciseAction(requestedWindow, operation: "windowClose") |
| | | func close(window: AlignerWindow) throws -> WindowCloseResult { |
| | | DevelopmentDiagnostics.log("windowClose.window.start", [ |
| | | "windowID": window.id, |
| | | "identifierSource": String(describing: window.identifierSource), |
| | |
| | | processIdentifier: matchedAXWindow.processIdentifier, |
| | | attempt: "initial" |
| | | ) |
| | | } |
| | | |
| | | private func refreshedFinderWindowForPreciseAction( |
| | | _ window: AlignerWindow, |
| | | operation: String |
| | | ) -> AlignerWindow { |
| | | guard isFinderCGOnlyPageCandidate(window) else { return window } |
| | | |
| | | guard let refreshedWindow = try? allWindows().first(where: { refreshed in |
| | | refreshed.id == window.id |
| | | && refreshed.identifierSource == window.identifierSource |
| | | && refreshed.app.bundleIdentifier == window.app.bundleIdentifier |
| | | }) else { |
| | | DevelopmentDiagnostics.log("\(operation).finderRefresh.missing", [ |
| | | "windowID": window.id, |
| | | "spaceIDCount": window.spaceIDs.count, |
| | | "hasFrame": window.frame != nil |
| | | ]) |
| | | return window |
| | | } |
| | | |
| | | let mergedWindow = AlignerWindow( |
| | | id: window.id, |
| | | app: window.app.processIdentifier == nil ? refreshedWindow.app : window.app, |
| | | title: window.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty |
| | | ? refreshedWindow.title |
| | | : window.title, |
| | | identifierSource: window.identifierSource, |
| | | isMinimized: window.isMinimized || refreshedWindow.isMinimized, |
| | | isFullscreen: window.isFullscreen || refreshedWindow.isFullscreen, |
| | | isGhost: window.isGhost, |
| | | frame: window.frame ?? refreshedWindow.frame, |
| | | spaceIDs: window.spaceIDs.isEmpty ? refreshedWindow.spaceIDs : window.spaceIDs |
| | | ) |
| | | |
| | | if mergedWindow.spaceIDs != window.spaceIDs |
| | | || mergedWindow.isFullscreen != window.isFullscreen |
| | | || (window.frame == nil && refreshedWindow.frame != nil) { |
| | | DevelopmentDiagnostics.log("\(operation).finderRefresh.merged", [ |
| | | "windowID": window.id, |
| | | "originalSpaceIDCount": window.spaceIDs.count, |
| | | "refreshedSpaceIDCount": refreshedWindow.spaceIDs.count, |
| | | "mergedSpaceIDCount": mergedWindow.spaceIDs.count, |
| | | "originalIsFullscreen": window.isFullscreen, |
| | | "refreshedIsFullscreen": refreshedWindow.isFullscreen, |
| | | "mergedIsFullscreen": mergedWindow.isFullscreen, |
| | | "originalHasFrame": window.frame != nil, |
| | | "refreshedHasFrame": refreshedWindow.frame != nil, |
| | | "mergedHasFrame": mergedWindow.frame != nil |
| | | ]) |
| | | } |
| | | |
| | | return mergedWindow |
| | | } |
| | | |
| | | private func closeButton(for axWindow: AXWindowMetadata) -> AXUIElement? { |
| | |
| | | } |
| | | |
| | | guard let host = finderTabHostAXWindow(for: window, in: axMetadata) else { |
| | | let normalizedTargetTitle = normalizedWindowTitle(window.title) |
| | | let applicationActivated = activateApplication(processIdentifier: processIdentifier) |
| | | let selectedViaWindowMenu = !normalizedTargetTitle.isEmpty |
| | | && selectFinderWindowMenuItem( |
| | | processIdentifier: processIdentifier, |
| | | window: window, |
| | | normalizedTargetTitle: normalizedTargetTitle |
| | | ) |
| | | DevelopmentDiagnostics.log("\(operation).finderTab.hostlessWindowMenuResult", [ |
| | | "windowID": window.id, |
| | | "pid": processIdentifier, |
| | | "reason": reason, |
| | | "applicationActivated": applicationActivated, |
| | | "selectedViaWindowMenu": selectedViaWindowMenu, |
| | | "spaceIDs": window.spaceIDs, |
| | | "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title), |
| | | "targetTitleLength": window.title.count |
| | | ]) |
| | | if selectedViaWindowMenu { |
| | | return true |
| | | } |
| | | |
| | | DevelopmentDiagnostics.log("\(operation).finderTab.hostMissing", [ |
| | | "windowID": window.id, |
| | | "pid": processIdentifier, |
| | |
| | | finderHostCanRepresentTarget(window, in: metadata) |
| | | } |
| | | |
| | | let hostCandidates: [AXWindowMetadata] |
| | | if targetSpaceIDs.isEmpty { |
| | | guard !selectableHostCandidates.isEmpty else { |
| | | DevelopmentDiagnostics.log("windowActivation.finderTab.noSelectableHost", [ |
| | | "windowID": window.id, |
| | | "candidateCount": candidates.count, |
| | | "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title), |
| | | "targetTitleLength": window.title.count |
| | | ]) |
| | | return nil |
| | | } |
| | | hostCandidates = selectableHostCandidates |
| | | } else { |
| | | hostCandidates = selectableHostCandidates.isEmpty ? spaceScopedCandidates : selectableHostCandidates |
| | | } |
| | | let hostCandidates = selectableHostCandidates.isEmpty |
| | | ? spaceScopedCandidates |
| | | : selectableHostCandidates |
| | | |
| | | let targetFrame = matchingFrame(for: window) |
| | | let scoredHosts = hostCandidates.compactMap { metadata -> (metadata: AXWindowMetadata, score: CGFloat)? in |
| | |
| | | |
| | | if let best = scoredHosts.first { |
| | | if scoredHosts.count == 1 { |
| | | if selectableHostCandidates.isEmpty { |
| | | DevelopmentDiagnostics.log("windowActivation.finderTab.frameInferredHost", [ |
| | | "windowID": window.id, |
| | | "hostWindowID": best.metadata.windowID, |
| | | "score": Double(best.score), |
| | | "spaceIDCount": targetSpaceIDs.count, |
| | | "candidateCount": hostCandidates.count, |
| | | "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title), |
| | | "targetTitleLength": window.title.count |
| | | ]) |
| | | } |
| | | return best.metadata |
| | | } |
| | | |
| | | let secondBest = scoredHosts[1] |
| | | if best.score - secondBest.score >= 0.05 { |
| | | if selectableHostCandidates.isEmpty { |
| | | DevelopmentDiagnostics.log("windowActivation.finderTab.frameInferredHost", [ |
| | | "windowID": window.id, |
| | | "hostWindowID": best.metadata.windowID, |
| | | "score": Double(best.score), |
| | | "secondBestScore": Double(secondBest.score), |
| | | "spaceIDCount": targetSpaceIDs.count, |
| | | "candidateCount": hostCandidates.count, |
| | | "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title), |
| | | "targetTitleLength": window.title.count |
| | | ]) |
| | | } |
| | | return best.metadata |
| | | } |
| | | } |
| | | |
| | | if hostCandidates.count == 1 { |
| | | if selectableHostCandidates.isEmpty { |
| | | DevelopmentDiagnostics.log("windowActivation.finderTab.singleFrameHostFallback", [ |
| | | "windowID": window.id, |
| | | "hostWindowID": hostCandidates[0].windowID, |
| | | "spaceIDCount": targetSpaceIDs.count, |
| | | "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title), |
| | | "targetTitleLength": window.title.count |
| | | ]) |
| | | } |
| | | return hostCandidates[0] |
| | | } |
| | | |