ux: bulk WIP — UxPlugin→XPlugin rename + new anim/core/navi/reactive packages

Catch-all commit for outstanding pre-existing local changes. Mixes
several themes that would normally be split:

- Rename: UxPlugin → XPlugin across iOS, macOS, Android registrants.
- New top-level packages under lib/src/: anim/ (animated values,
  panes, sheets, dock, measured), core/ (Emitter, ReactiveBuilder
  scaffolding, presenter/widget/value/dispose primitives), navi/
  (Screen/ScreenStack/Router/hero/transitions), reactive/.
- Edits across existing plugins (clipboard, crash, file, gallery,
  keyboard, scanner, sensor, url) to align with the new core.
- Test updates and CHANGELOG/README touches accompanying the above.
This commit is contained in:
agra
2026-05-21 08:58:07 +03:00
parent a508aca2bb
commit d68a2978eb
83 changed files with 5006 additions and 275 deletions

View File

@@ -15,7 +15,7 @@ import AVFoundation
/// videoOrientation = .landscapeRight"). Prefer the new API where
/// available, fall back to the deprecated one for older macOS.
extension AVCaptureConnection {
func applyUxCaptureOrientation(_ orientation: DeviceOrientationFlutter) {
func applyXCaptureOrientation(_ orientation: DeviceOrientationFlutter) {
// Pin to 0° rotation (`.landscapeRight`) on macOS desktop
// cameras are physically landscape and any non-zero rotation
// physically rotates the buffer. Diagnostic build confirmed

View File

@@ -106,7 +106,7 @@ public class FilePlugin: NSObject, NativePlugin {
panel.allowedFileTypes = utis
}
let host = UxWindow.flutterView?.window
let host = XWindow.flutterView?.window
let completion: (NSApplication.ModalResponse) -> Void = { response in
guard response == .OK, let url = panel.url else {
result(nil)
@@ -173,7 +173,7 @@ public class FilePlugin: NSObject, NativePlugin {
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 {
guard let view = XWindow.flutterView else {
return result(FlutterError(code: "no_view", message: "no Flutter view", details: nil))
}
@@ -314,10 +314,10 @@ public class FilePlugin: NSObject, NativePlugin {
withScope(path: path, bookmark: bookmarkData) { url in
// Prefer in-app Quick Look (keeps the host app in the foreground).
// Fall back to NSWorkspace.open if there's no window to host the panel.
if let flutterView = UxWindow.flutterView,
if let flutterView = XWindow.flutterView,
let window = flutterView.window,
let panel = QLPreviewPanel.shared() {
let responder = UxQLPreviewResponder(url: url, window: window)
let responder = XQLPreviewResponder(url: url, window: window)
flutterView.addSubview(responder)
window.makeFirstResponder(responder)
panel.updateController()
@@ -344,7 +344,7 @@ fileprivate func mimeFromExtension(_ ext: String) -> String? {
return mime as String
}
private final class UxQLPreviewResponder: NSView, QLPreviewPanelDataSource {
private final class XQLPreviewResponder: NSView, QLPreviewPanelDataSource {
let url: URL
private weak var previousFirstResponder: NSResponder?
private weak var previousWindow: NSWindow?

View File

@@ -5,7 +5,7 @@ public protocol NativePlugin {
func register(with registrar: FlutterPluginRegistrar)
}
public enum UxWindow {
public enum XWindow {
public static var keyWindow: NSWindow? {
NSApp.keyWindow ?? NSApp.mainWindow
}

View File

@@ -1,7 +1,7 @@
import FlutterMacOS
import AppKit
public class UxPlugin: NSObject, FlutterPlugin {
public class XPlugin: NSObject, FlutterPlugin {
private static var plugins: [NativePlugin] = []
public static func register(with registrar: FlutterPluginRegistrar) {

View File

@@ -1,4 +1,4 @@
// Native data detection for UxUrl. Synchronous, callable via dart:ffi.
// Native data detection for XUrl. Synchronous, callable via dart:ffi.
//
// Exports two symbols:
// uint8_t* ux_match_url(const uint16_t* utf16, int32_t len, int32_t* out_size);
@@ -24,13 +24,13 @@ static const uint32_t kKindWeb = 0;
static const uint32_t kKindEmail = 1;
static const uint32_t kKindPhone = 2;
@interface UxUrlRawMatch : NSObject
@interface XUrlRawMatch : NSObject
@property (nonatomic) int32_t start;
@property (nonatomic) int32_t end;
@property (nonatomic) uint32_t kind;
@property (nonatomic, copy) NSData *urlUtf8;
@end
@implementation UxUrlRawMatch
@implementation XUrlRawMatch
@end
static NSDataDetector *ux_url_data_detector(void) {
@@ -76,7 +76,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
if (text.length == 0) return NULL;
NSRange whole = NSMakeRange(0, text.length);
NSMutableArray<UxUrlRawMatch *> *raws = [NSMutableArray array];
NSMutableArray<XUrlRawMatch *> *raws = [NSMutableArray array];
NSDataDetector *detector = ux_url_data_detector();
if (detector != nil) {
@@ -130,7 +130,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
}
if (url.length == 0) return;
UxUrlRawMatch *m = [[UxUrlRawMatch alloc] init];
XUrlRawMatch *m = [[XUrlRawMatch alloc] init];
m.start = (int32_t)r.location;
m.end = (int32_t)(r.location + r.length);
m.kind = kind;
@@ -152,7 +152,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
if (r.location == NSNotFound || r.length == 0) return;
NSString *substr = [text substringWithRange:r];
NSString *withScheme = [@"http://" stringByAppendingString:substr];
UxUrlRawMatch *m = [[UxUrlRawMatch alloc] init];
XUrlRawMatch *m = [[XUrlRawMatch alloc] init];
m.start = (int32_t)r.location;
m.end = (int32_t)(r.location + r.length);
m.kind = kKindWeb;
@@ -164,7 +164,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
if (raws.count == 0) return NULL;
// Sort: start asc, then length desc, then kind desc (phone > email > web on tie).
[raws sortUsingComparator:^NSComparisonResult(UxUrlRawMatch *a, UxUrlRawMatch *b) {
[raws sortUsingComparator:^NSComparisonResult(XUrlRawMatch *a, XUrlRawMatch *b) {
if (a.start != b.start) return a.start < b.start ? NSOrderedAscending : NSOrderedDescending;
int32_t la = a.end - a.start;
int32_t lb = b.end - b.start;
@@ -174,10 +174,10 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
}];
// Greedy de-overlap.
NSMutableArray<UxUrlRawMatch *> *kept = [NSMutableArray arrayWithCapacity:raws.count];
NSMutableArray<XUrlRawMatch *> *kept = [NSMutableArray arrayWithCapacity:raws.count];
int32_t lastEnd = 0;
BOOL haveAny = NO;
for (UxUrlRawMatch *m in raws) {
for (XUrlRawMatch *m in raws) {
if (haveAny && m.start < lastEnd) continue;
[kept addObject:m];
lastEnd = m.end;
@@ -185,7 +185,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
}
NSUInteger total = 4;
for (UxUrlRawMatch *m in kept) {
for (XUrlRawMatch *m in kept) {
total += 16 + (NSUInteger)m.urlUtf8.length;
}
@@ -194,7 +194,7 @@ uint8_t *ux_match_url(const uint16_t *utf16, int32_t len, int32_t *out_size) {
uint32_t cnt = (uint32_t)kept.count;
memcpy(buf, &cnt, 4);
NSUInteger off = 4;
for (UxUrlRawMatch *m in kept) {
for (XUrlRawMatch *m in kept) {
int32_t start = m.start;
int32_t end = m.end;
uint32_t kind = m.kind;