files
This commit is contained in:
117
macos/Classes/FilePlugin.swift
Normal file
117
macos/Classes/FilePlugin.swift
Normal file
@@ -0,0 +1,117 @@
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
import Quartz
|
||||
|
||||
public class FilePlugin: NSObject, NativePlugin {
|
||||
private var channel: FlutterMethodChannel?
|
||||
|
||||
public func register(with registrar: FlutterPluginRegistrar) {
|
||||
let c = FlutterMethodChannel(name: "ux/file", binaryMessenger: registrar.messenger)
|
||||
c.setMethodCallHandler { [weak self] call, result in
|
||||
self?.handle(call, result: result)
|
||||
}
|
||||
channel = c
|
||||
}
|
||||
|
||||
private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "share": handleShare(call, result: result)
|
||||
case "open": handleOpen(call, result: result)
|
||||
default: result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleShare(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
guard let args = call.arguments as? [String: Any],
|
||||
let path = args["path"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "path is required", details: nil))
|
||||
}
|
||||
guard let view = UxWindow.flutterView else {
|
||||
return result(FlutterError(code: "no_view", message: "no Flutter view", details: nil))
|
||||
}
|
||||
|
||||
let url = URL(fileURLWithPath: path)
|
||||
let picker = NSSharingServicePicker(items: [url])
|
||||
|
||||
let rect: NSRect
|
||||
if let r = args["sourceRect"] as? [String: Any],
|
||||
let x = (r["x"] as? NSNumber)?.doubleValue,
|
||||
let y = (r["y"] as? NSNumber)?.doubleValue,
|
||||
let w = (r["w"] as? NSNumber)?.doubleValue,
|
||||
let h = (r["h"] as? NSNumber)?.doubleValue {
|
||||
if view.isFlipped {
|
||||
rect = NSRect(x: x, y: y, width: w, height: h)
|
||||
} else {
|
||||
rect = NSRect(x: x, y: view.bounds.height - y - h, width: w, height: h)
|
||||
}
|
||||
} else {
|
||||
rect = NSRect(x: view.bounds.midX, y: view.bounds.midY, width: 1, height: 1)
|
||||
}
|
||||
|
||||
picker.show(relativeTo: rect, of: view, preferredEdge: .minY)
|
||||
result(true)
|
||||
}
|
||||
|
||||
private func handleOpen(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
guard let args = call.arguments as? [String: Any],
|
||||
let path = args["path"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "path is required", details: nil))
|
||||
}
|
||||
let url = URL(fileURLWithPath: path)
|
||||
|
||||
// Prefer in-app Quick Look (keeps Banlu in the foreground).
|
||||
// Fall back to NSWorkspace.open if there's no window to host the panel.
|
||||
if let flutterView = UxWindow.flutterView,
|
||||
let window = flutterView.window,
|
||||
let panel = QLPreviewPanel.shared() {
|
||||
let responder = UxQLPreviewResponder(url: url, window: window)
|
||||
flutterView.addSubview(responder)
|
||||
window.makeFirstResponder(responder)
|
||||
panel.updateController()
|
||||
panel.makeKeyAndOrderFront(nil)
|
||||
result(true)
|
||||
return
|
||||
}
|
||||
|
||||
result(NSWorkspace.shared.open(url))
|
||||
}
|
||||
}
|
||||
|
||||
private final class UxQLPreviewResponder: NSView, QLPreviewPanelDataSource {
|
||||
let url: URL
|
||||
private weak var previousFirstResponder: NSResponder?
|
||||
private weak var previousWindow: NSWindow?
|
||||
|
||||
init(url: URL, window: NSWindow) {
|
||||
self.url = url
|
||||
self.previousWindow = window
|
||||
self.previousFirstResponder = window.firstResponder
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override var acceptsFirstResponder: Bool { true }
|
||||
|
||||
override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel) -> Bool { true }
|
||||
|
||||
override func beginPreviewPanelControl(_ panel: QLPreviewPanel) {
|
||||
panel.dataSource = self
|
||||
}
|
||||
|
||||
override func endPreviewPanelControl(_ panel: QLPreviewPanel) {
|
||||
panel.dataSource = nil
|
||||
let win = previousWindow
|
||||
let prev = previousFirstResponder
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
win?.makeFirstResponder(prev)
|
||||
self?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int { 1 }
|
||||
|
||||
func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! {
|
||||
url as QLPreviewItem
|
||||
}
|
||||
}
|
||||
18
macos/Classes/NativePlugin.swift
Normal file
18
macos/Classes/NativePlugin.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
|
||||
public protocol NativePlugin {
|
||||
func register(with registrar: FlutterPluginRegistrar)
|
||||
}
|
||||
|
||||
public enum UxWindow {
|
||||
public static var keyWindow: NSWindow? {
|
||||
NSApp.keyWindow ?? NSApp.mainWindow
|
||||
}
|
||||
|
||||
/// FlutterViewController's view. Used as the anchor for
|
||||
/// `NSSharingServicePicker` popovers.
|
||||
public static var flutterView: NSView? {
|
||||
keyWindow?.contentViewController?.view
|
||||
}
|
||||
}
|
||||
19
macos/Classes/UxPlugin.swift
Normal file
19
macos/Classes/UxPlugin.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
import FlutterMacOS
|
||||
import AppKit
|
||||
|
||||
public class UxPlugin: NSObject, FlutterPlugin {
|
||||
private static var plugins: [NativePlugin] = []
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
plugins = [
|
||||
FilePlugin(),
|
||||
]
|
||||
for plugin in plugins {
|
||||
plugin.register(with: registrar)
|
||||
}
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user