Mac에서 윈도우를 좋은 느낌으로 배치하는 툴 재현해 보았다.

9433 단어 SwiftMac
BetterSnapToolMagnet과 같은 macOS 용 윈도우 배치 관리자 도구를 재현 해 보았습니다. 현재의 최전면의 윈도우를 오른쪽 절반에 배치하거나, ​​왼쪽 3분의 1에 배치하거나, ​​최대한 넓혀 배치하거나 하는 것이 메뉴 바의 커멘드나 쇼트 컷 키를 트리거로서 실시할 수 있습니다.


동작의 모습


동기



원래 일반 앱이라고 하는 것은 SandBox에 지켜지고 있기 때문에, 외부의 앱의 관할인 윈도우를 제어하는 ​​것은 본래 할 수 없습니다. 상주형 앱인 것, 다른 앱의 윈도우를 제어하고 있는 것에 흥미가 있었기 때문에 어떠한 기술로 이것이 실현되고 있는지 조사하고 싶었습니다.

기술에 관하여



앱 상주



상주형 앱을 만드는 방법에 대해서는 별도 기사가 있으므로 이쪽을 참고로.
htps // // 천. V / Kyome / Archi C / s / 02d9f969fd17E5

창 제어



우선, 최전면에 있는 윈도우의 앱을 취득하려면 , NSWorkspace 의 API 를 이용하면 됩니다.
let app = NSWorkspace.shared.runningApplications.first(where: { $0.isActive })

그리고는, ApplicationServices (을)를 개입시켜 접근성의 API 를 사용해 윈도우를 제어했습니다. AXUIElement라는 개체로 창 정보를 가져오거나 덮어쓸 수 있습니다. 위에서 얻은 앱의 pid를 사용하여 AXUIElement를 생성할 수 있습니다.
AXUIElementCopyAttributeValue라는 API를 사용하면 지정된 AUIElement 정보를 얻을 수 있습니다.
// 頻繁に使うため便利関数化
func copyAttributeValue(_ element: AXUIElement, attribute: String) -> CFTypeRef? {
    var ref: CFTypeRef? = nil
    let error = AXUIElementCopyAttributeValue(element, attribute as CFString, &ref)
    if error == .success {
        return ref
    }
    return .none
}

// 情報を取得する例
// ウィンドウの役割をStringで取得
func getRole(element: AXUIElement) -> String? {
    return self.copyAttributeValue(element, attribute: kAXRoleAttribute) as? String
}

// ウィンドウがフルスクリーンかどうかをBoolで取得
func isFullscreen(element: AXUIElement) -> Bool {
    let result = self.copyAttributeValue(element, attribute: "AXFullScreen") as? NSNumber
    return result?.boolValue ?? false
}

attribute 에 kAX 로 시작하는 키를 지정해 CFTypeRef = AnyObject 형태의 정보를 취득해, 나중에 적절한 형태로 변환합니다. 윈도우의 좌표나 크기 등의 정보를 CGPointCGSize 로 취득하는 경우는, 한층 더 AXValueGetValue 라고 하는 API 를 거듭해 사용할 필요가 있습니다. GtiHub에서 코드를 검색하면 어떻게하는지 알 수 있습니다.
AUIElement의 정보를 덮어쓰려면 AXUIElementSetAttributeValue라는 API를 사용합니다.
// 頻繁に使うため便利関数化
func setAttributeValue(_ element: AXUIElement, attribute: String, value: CFTypeRef) -> Bool {
    let error = AXUIElementSetAttributeValue(element, attribute as CFString, value)
    return error == .success
}

// 情報を上書きする例
// 座標を更新
@discardableResult
func setPosition(element: AXUIElement, position: CGPoint) -> Bool {
    var position = position
    if let value = AXValueCreate(AXValueType.cgPoint, &position) {
        return self.setAttributeValue(element, attribute: kAXPositionAttribute, value: value)
    }
    return false
}

업데이트하려는 값을 AXValue 형식으로 변환한 다음 kAX로 시작하는 키와 함께 전달하면 업데이트할 수 있습니다. 창 통제의 근간은 이런 느낌입니다.

제한사항


  • 현재는 App SandBox 환경에서 내게 필요한 옵션 API를 두드리지 않기 때문입니다. SandBox를 제거해야합니다. 필연적으로 App Store에서 배포하는 것은 불가능합니다. BetterSnapTool과 Magnet은 규제가 엄격하지 않았던 시절부터 출시되었기 때문에 특별히 허용되고 있다는 느낌일까요? 단지 원래 바이너리 제출에서 거부될 것 같기 때문에 특별한 App Sandbox Temporary Exception Entitlements를 지정하고 있을지도 모릅니다.
  • 내게 필요한 옵션 API를 두드리려면 사용자에게 전용 권한을 부여해야 합니다. 시스템 환경설정 -> 보안 및 개인정보 보호 -> 내게 필요한 옵션 항목에서 앱별로 권한을 전환할 수 있습니다. 따라서 현재 권한이 부여되어 있는지 확인하고 사용자에게 촉구하는 메커니즘이 필요합니다.

  • 배포



    GitHub에 소스도 앱도 공개하고 있습니다.
    더 커스터마이즈하고 싶은 분의 커밋을 기다리고 있습니다.
    htps : // 기주 b. 코 m / 쿄메 22 / 시 f와 텐도 w

    좋은 웹페이지 즐겨찾기