@legendapp/spark 0.0.1-next.2 → 0.0.1-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -4
- package/ai-codex.ts +1 -0
- package/ai.ts +1 -0
- package/app-documents.ts +1 -0
- package/config-plugin.d.cts +2 -0
- package/contracts.ts +2 -0
- package/diagnostics.ts +1 -0
- package/expo-config.d.cts +1 -0
- package/metro.cjs +4 -1
- package/metro.d.cts +12 -0
- package/native.cjs +3 -1
- package/native.d.cts +6 -0
- package/package.json +82 -14
- package/schema.json +276 -58
- package/settings-observable.ts +1 -0
- package/settings-window.ts +1 -0
- package/shortcuts-commands.ts +1 -0
- package/shortcuts-keyboard.ts +3 -0
- package/ui-classnames.ts +1 -0
- package/ui-glass.ts +1 -0
- package/ui-search.ts +1 -0
- package/ui-sidebar.ts +1 -0
- package/ui-split-view.ts +1 -0
- package/ui-symbol.ts +1 -0
- package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
- package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
- package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
- package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
- package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
- package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
- package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
- package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
- package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
- package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
- package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
- package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
- package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
- package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
- package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
- package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
- package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
- package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
- package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
- package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
- package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
- package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
- package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
- package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
- package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
- package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
- package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
- package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
- package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
- package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
- package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
- package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
- package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
- package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
- package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
- package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
- package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
- package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
- package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
- package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
- package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
- package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
- package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
- package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
- package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
- package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
- package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
- package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
- package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
- package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
- package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
- package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
- package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
- package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
- package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
- package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
- package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
- package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
- package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
- package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
- package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
- package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
- package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
- package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
- package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
- package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
- package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
- package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
- package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
- package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
- package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
- package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
- package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
- package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
- package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
- package/windows-macos.ts +1 -0
- package/expo-metro.cjs +0 -1
- package/message-dialog.ts +0 -1
- package/universal.cjs +0 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
|
@@ -40,7 +40,7 @@ struct WindowResources : winrt::implements<WindowResources, RNW::Composition::IC
|
|
|
40
40
|
void ResourcesChanged(winrt::event_token const &token) noexcept { changed.remove(token); }
|
|
41
41
|
void SetTheme(int value) { theme = value; changed(*this, nullptr); }
|
|
42
42
|
};
|
|
43
|
-
struct Window { RNW::ReactNativeWindow view{nullptr}; UI::AppWindow app{nullptr}; UI::OverlappedPresenter normal{nullptr};
|
|
43
|
+
struct Window { RNW::ReactNativeWindow view{nullptr}; UI::AppWindow app{nullptr}; UI::OverlappedPresenter normal{nullptr}; double minWidth = 100, minHeight = 100, maxWidth = 20000, maxHeight = 20000; std::string instanceId, guardId; std::vector<RNW::ReactPromise<std::string>> closeWaiters; std::string parentId; bool modal = false; bool overlay = false; bool transparent = false; bool shadow = true; bool disabledByModal = false; bool enabledBeforeModal = false; bool guard = false; bool pending = false; bool allow = false; uint64_t request = 0; HMONITOR monitor = nullptr; bool fullscreen = false; bool closable = true; bool restoreFrame = false; int theme = -1; winrt::com_ptr<WindowResources> resources; winrt::Microsoft::UI::Composition::SpriteVisual background{nullptr}; };
|
|
44
44
|
static std::map<std::string, Window> windows;
|
|
45
45
|
static RNW::ReactNativeHost host{nullptr};
|
|
46
46
|
static winrt::Microsoft::UI::Composition::Compositor compositor{nullptr};
|
|
@@ -50,7 +50,9 @@ static void QueueNotificationResponse(std::string const &);
|
|
|
50
50
|
static std::string NotificationResponses();
|
|
51
51
|
static void EmitNotificationEvents();
|
|
52
52
|
static void SetNotificationWindow(HWND);
|
|
53
|
-
static bool
|
|
53
|
+
static bool quitPending = false, quitAccepted = false;
|
|
54
|
+
static std::set<std::string> quitGuards, pendingQuitGuards;
|
|
55
|
+
static std::vector<RNW::ReactPromise<std::string>> quitWaiters;
|
|
54
56
|
static uint64_t generation = 0, quitRequest = 0;
|
|
55
57
|
static Json::JsonArray pendingURLs{nullptr};
|
|
56
58
|
static std::string initialURL;
|
|
@@ -64,7 +66,6 @@ static std::map<std::string, Shortcut> shortcuts;
|
|
|
64
66
|
static HHOOK keyboardHook = nullptr;
|
|
65
67
|
static std::map<UINT, RNW::JSValueObject> menuActions;
|
|
66
68
|
static HMENU currentMenu = nullptr;
|
|
67
|
-
static std::string menuOwner;
|
|
68
69
|
static Json::JsonArray menus{nullptr};
|
|
69
70
|
static HACCEL acceleratorTable = nullptr;
|
|
70
71
|
static HWND Hwnd(Window const &window) { return winrt::Microsoft::UI::GetWindowFromWindowId(window.app.Id()); }
|
|
@@ -77,7 +78,29 @@ static void UpdateModality() {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
static std::string Id(HWND hwnd) { auto root = GetAncestor(hwnd, GA_ROOT); for (auto const &[id, window] : windows) if (Hwnd(window) == root) return id; return ""; }
|
|
80
|
-
static
|
|
81
|
+
static Json::JsonObject Info(std::string const &id);
|
|
82
|
+
static RNW::JSValue Value(Json::IJsonValue const &value);
|
|
83
|
+
static void FinishClose(std::string const &id, bool closed) {
|
|
84
|
+
auto found = windows.find(id); if (found == windows.end()) return;
|
|
85
|
+
auto waiters = std::move(found->second.closeWaiters); found->second.closeWaiters.clear();
|
|
86
|
+
for (auto const &waiter : waiters) waiter.Resolve(closed ? "{\"closed\":true}" : "{\"closed\":false,\"reason\":\"vetoed\"}");
|
|
87
|
+
}
|
|
88
|
+
static void Emit(RNW::JSValueObject value) {
|
|
89
|
+
auto id = value.find("windowId"), type = value.find("type");
|
|
90
|
+
if (id != value.end() && type != value.end()) {
|
|
91
|
+
auto found = windows.find(id->second.AsString());
|
|
92
|
+
if (found != windows.end()) {
|
|
93
|
+
const auto name = type->second.AsString();
|
|
94
|
+
value["instanceId"] = found->second.instanceId;
|
|
95
|
+
if (name == "beforeClose" || name == "closeGuardTimeout") value["guardId"] = found->second.guardId;
|
|
96
|
+
if (name == "move" || name == "resize" || name == "screenChanged") { value["type"] = "boundsChanged"; value["bounds"] = Value(Info(found->first).GetNamedObject(L"bounds")); }
|
|
97
|
+
else if (name == "focus" || name == "blur") { value["type"] = "focusChanged"; value["focused"] = name == "focus"; }
|
|
98
|
+
else if (name == "enterFullscreen" || name == "leaveFullscreen") { value["type"] = "fullscreenChanged"; value["fullscreen"] = name == "enterFullscreen"; }
|
|
99
|
+
else if (name == "visibilityChanged") value["visible"] = !!IsWindowVisible(Hwnd(found->second));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (context) context.EmitJSEvent(L"RCTDeviceEventEmitter", L"desktop", std::move(value));
|
|
103
|
+
}
|
|
81
104
|
static void WindowTransitions(std::string const &id) {
|
|
82
105
|
auto found = windows.find(id); if (found == windows.end()) return;
|
|
83
106
|
auto &window = found->second;
|
|
@@ -112,52 +135,91 @@ static void WriteJson(RNW::IJSValueWriter const &writer, Json::IJsonValue const
|
|
|
112
135
|
default: writer.WriteNull();
|
|
113
136
|
}
|
|
114
137
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
result.SetNamedValue(L"id", Json::JsonValue::CreateStringValue(to_hstring(id))); result.SetNamedValue(L"title", Json::JsonValue::CreateStringValue(window.app.Title()));
|
|
119
|
-
result.SetNamedValue(L"kind", Json::JsonValue::CreateStringValue(window.overlay ? L"overlay" : L"window"));
|
|
120
|
-
result.SetNamedValue(L"visible", Json::JsonValue::CreateBooleanValue(IsWindowVisible(hwnd))); result.SetNamedValue(L"focused", Json::JsonValue::CreateBooleanValue(GetForegroundWindow() == hwnd));
|
|
121
|
-
result.SetNamedValue(L"minimized", Json::JsonValue::CreateBooleanValue(IsIconic(hwnd)));
|
|
122
|
-
result.SetNamedValue(L"resizable", Json::JsonValue::CreateBooleanValue((GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME) != 0));
|
|
123
|
-
result.SetNamedValue(L"alwaysOnTop", Json::JsonValue::CreateBooleanValue((GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0));
|
|
124
|
-
result.SetNamedValue(L"fullscreen", Json::JsonValue::CreateBooleanValue(window.app.Presenter().Kind() == UI::AppWindowPresenterKind::FullScreen));
|
|
125
|
-
result.SetNamedValue(L"minWidth", Json::JsonValue::CreateNumberValue(window.minWidth)); result.SetNamedValue(L"maxWidth", Json::JsonValue::CreateNumberValue(window.maxWidth));
|
|
126
|
-
rect.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue(frame.left)); rect.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue(frame.top)); rect.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue(frame.right-frame.left)); rect.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue(frame.bottom-frame.top)); result.SetNamedValue(L"frame", rect); return result;
|
|
127
|
-
}
|
|
128
|
-
static Json::JsonObject Rect(RECT const &frame) {
|
|
129
|
-
Json::JsonObject result;
|
|
130
|
-
result.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue(frame.left));
|
|
131
|
-
result.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue(frame.top));
|
|
132
|
-
result.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue(frame.right - frame.left));
|
|
133
|
-
result.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue(frame.bottom - frame.top));
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
static Json::JsonArray Displays() {
|
|
137
|
-
struct State { Json::JsonArray result; std::exception_ptr error; } state;
|
|
138
|
+
struct DisplayGeometry { HMONITOR monitor; std::wstring id, persistentId, name; RECT frame, work; double scale; bool primary; };
|
|
139
|
+
static std::vector<DisplayGeometry> DisplayGeometries() {
|
|
140
|
+
struct State { std::vector<DisplayGeometry> result; std::exception_ptr error; } state;
|
|
138
141
|
const auto enumerated = EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR monitor, HDC, LPRECT, LPARAM param) -> BOOL {
|
|
139
142
|
auto &state = *reinterpret_cast<State *>(param);
|
|
140
143
|
try {
|
|
141
144
|
MONITORINFOEXW info{}; info.cbSize = sizeof(info);
|
|
142
145
|
if (!GetMonitorInfoW(monitor, reinterpret_cast<MONITORINFO *>(&info))) winrt::throw_last_error();
|
|
143
|
-
UINT
|
|
144
|
-
DISPLAY_DEVICEW device{}; device.cb = sizeof(device); EnumDisplayDevicesW(info.szDevice, 0, &device,
|
|
145
|
-
|
|
146
|
-
display.SetNamedValue(L"id", Json::JsonValue::CreateStringValue(info.szDevice));
|
|
147
|
-
display.SetNamedValue(L"name", Json::JsonValue::CreateStringValue(device.DeviceString[0] ? device.DeviceString : info.szDevice));
|
|
148
|
-
display.SetNamedValue(L"frame", Rect(info.rcMonitor)); display.SetNamedValue(L"workArea", Rect(info.rcWork));
|
|
149
|
-
display.SetNamedValue(L"scale", Json::JsonValue::CreateNumberValue(dpiX / 96.0)); state.result.Append(display);
|
|
146
|
+
UINT x = 96, y = 96; winrt::check_hresult(GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y));
|
|
147
|
+
DISPLAY_DEVICEW device{}; device.cb = sizeof(device); EnumDisplayDevicesW(info.szDevice, 0, &device, EDD_GET_DEVICE_INTERFACE_NAME);
|
|
148
|
+
state.result.push_back({monitor, info.szDevice, device.DeviceID, device.DeviceString[0] ? device.DeviceString : info.szDevice, info.rcMonitor, info.rcWork, x / 96.0, (info.dwFlags & MONITORINFOF_PRIMARY) != 0});
|
|
150
149
|
return TRUE;
|
|
151
150
|
} catch (...) { state.error = std::current_exception(); return FALSE; }
|
|
152
151
|
}, reinterpret_cast<LPARAM>(&state));
|
|
153
152
|
if (state.error) std::rethrow_exception(state.error);
|
|
154
|
-
if (!enumerated) winrt::throw_last_error();
|
|
153
|
+
if (!enumerated || state.result.empty()) winrt::throw_last_error();
|
|
155
154
|
return state.result;
|
|
156
155
|
}
|
|
157
|
-
static
|
|
156
|
+
static DisplayGeometry OwningDisplay(RECT const &frame) {
|
|
157
|
+
auto displays = DisplayGeometries(); auto best = displays.front(); double area = -1;
|
|
158
|
+
for (auto const &display : displays) {
|
|
159
|
+
RECT intersection{}; IntersectRect(&intersection, &frame, &display.frame);
|
|
160
|
+
double next = static_cast<double>(intersection.right - intersection.left) * (intersection.bottom - intersection.top);
|
|
161
|
+
if (next > area || (next == area && display.primary)) { best = display; area = next; }
|
|
162
|
+
}
|
|
163
|
+
return best;
|
|
164
|
+
}
|
|
165
|
+
static DisplayGeometry DisplayWithId(winrt::hstring const &id) {
|
|
166
|
+
for (auto const &display : DisplayGeometries()) if (display.id == id.c_str()) return display;
|
|
167
|
+
throw winrt::hresult_error(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), L"Display is disconnected");
|
|
168
|
+
}
|
|
169
|
+
static Json::JsonObject LogicalRect(RECT const &rect, DisplayGeometry const &display) {
|
|
170
|
+
Json::JsonObject result;
|
|
171
|
+
result.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue((rect.left - display.frame.left) / display.scale));
|
|
172
|
+
result.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue((rect.top - display.frame.top) / display.scale));
|
|
173
|
+
result.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue((rect.right - rect.left) / display.scale));
|
|
174
|
+
result.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue((rect.bottom - rect.top) / display.scale));
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
static Json::JsonObject Info(std::string const &id) {
|
|
178
|
+
auto &window = windows.at(id); auto hwnd = Hwnd(window); RECT frame{}; if (!GetWindowRect(hwnd, &frame)) winrt::throw_last_error();
|
|
179
|
+
if (IsIconic(hwnd)) { WINDOWPLACEMENT placement{}; placement.length = sizeof(placement); if (GetWindowPlacement(hwnd, &placement)) { frame = placement.rcNormalPosition; auto display = OwningDisplay(frame); OffsetRect(&frame, display.work.left - display.frame.left, display.work.top - display.frame.top); } }
|
|
180
|
+
auto display = OwningDisplay(frame); auto bounds = LogicalRect(frame, display); bounds.SetNamedValue(L"displayId", Json::JsonValue::CreateStringValue(display.id));
|
|
181
|
+
Json::JsonObject result;
|
|
182
|
+
result.SetNamedValue(L"id", Json::JsonValue::CreateStringValue(to_hstring(id))); result.SetNamedValue(L"instanceId", Json::JsonValue::CreateStringValue(to_hstring(window.instanceId)));
|
|
183
|
+
result.SetNamedValue(L"title", Json::JsonValue::CreateStringValue(window.app.Title())); result.SetNamedValue(L"kind", Json::JsonValue::CreateStringValue(window.overlay ? L"overlay" : L"window"));
|
|
184
|
+
result.SetNamedValue(L"parentId", window.parentId.empty() ? Json::JsonValue::CreateNullValue() : Json::JsonValue::CreateStringValue(to_hstring(window.parentId)));
|
|
185
|
+
result.SetNamedValue(L"modal", Json::JsonValue::CreateBooleanValue(window.modal)); result.SetNamedValue(L"visible", Json::JsonValue::CreateBooleanValue(IsWindowVisible(hwnd)));
|
|
186
|
+
result.SetNamedValue(L"focused", Json::JsonValue::CreateBooleanValue(GetForegroundWindow() == hwnd)); result.SetNamedValue(L"minimized", Json::JsonValue::CreateBooleanValue(IsIconic(hwnd)));
|
|
187
|
+
result.SetNamedValue(L"maximized", Json::JsonValue::CreateBooleanValue(IsZoomed(hwnd) != FALSE));
|
|
188
|
+
result.SetNamedValue(L"fullscreen", Json::JsonValue::CreateBooleanValue(window.app.Presenter().Kind() == UI::AppWindowPresenterKind::FullScreen)); result.SetNamedValue(L"bounds", bounds);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
static Json::JsonArray Displays() {
|
|
192
|
+
Json::JsonArray result;
|
|
193
|
+
for (auto const &display : DisplayGeometries()) {
|
|
194
|
+
Json::JsonObject item, size;
|
|
195
|
+
item.SetNamedValue(L"id", Json::JsonValue::CreateStringValue(display.id)); item.SetNamedValue(L"persistentId", display.persistentId.empty() ? Json::JsonValue::CreateNullValue() : Json::JsonValue::CreateStringValue(display.persistentId));
|
|
196
|
+
item.SetNamedValue(L"name", Json::JsonValue::CreateStringValue(display.name)); item.SetNamedValue(L"primary", Json::JsonValue::CreateBooleanValue(display.primary));
|
|
197
|
+
size.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue((display.frame.right - display.frame.left) / display.scale)); size.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue((display.frame.bottom - display.frame.top) / display.scale));
|
|
198
|
+
item.SetNamedValue(L"size", size); item.SetNamedValue(L"workArea", LogicalRect(display.work, display)); item.SetNamedValue(L"scaleFactor", Json::JsonValue::CreateNumberValue(display.scale)); result.Append(item);
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
static void SetBounds(Window &window, Json::JsonObject const &bounds) {
|
|
203
|
+
auto display = DisplayWithId(bounds.GetNamedString(L"displayId"));
|
|
204
|
+
const double width = bounds.GetNamedNumber(L"width"), height = bounds.GetNamedNumber(L"height"), x = bounds.GetNamedNumber(L"x"), y = bounds.GetNamedNumber(L"y");
|
|
205
|
+
if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std::isfinite(height) || width < window.minWidth || height < window.minHeight || width > window.maxWidth || height > window.maxHeight || std::abs(x) > 10000000 || std::abs(y) > 10000000) throw winrt::hresult_invalid_argument(L"Bounds are outside window constraints");
|
|
206
|
+
window.app.MoveAndResize({static_cast<int32_t>(std::round(display.frame.left + x * display.scale)), static_cast<int32_t>(std::round(display.frame.top + y * display.scale)), static_cast<int32_t>(std::round(width * display.scale)), static_cast<int32_t>(std::round(height * display.scale))});
|
|
207
|
+
}
|
|
208
|
+
static Json::JsonObject CanonicalStyle(Json::JsonObject const &options) {
|
|
209
|
+
Json::JsonObject style;
|
|
210
|
+
for (auto key : {L"title", L"resizable", L"minimizable", L"alwaysOnTop", L"closable", L"appearance", L"backgroundColor", L"titleBarStyle", L"transparent", L"hasShadow"}) if (options.HasKey(key)) style.SetNamedValue(key, options.GetNamedValue(key));
|
|
211
|
+
for (auto key : {L"minSize", L"maxSize"}) if (options.HasKey(key)) {
|
|
212
|
+
bool minimum = std::wstring(key) == L"minSize"; auto value = options.GetNamedValue(key);
|
|
213
|
+
auto size = value.ValueType() == Json::JsonValueType::Null ? Json::JsonObject() : value.GetObject();
|
|
214
|
+
style.SetNamedValue(minimum ? L"minWidth" : L"maxWidth", Json::JsonValue::CreateNumberValue(size.GetNamedNumber(L"width", minimum ? 100 : 20000)));
|
|
215
|
+
style.SetNamedValue(minimum ? L"minHeight" : L"maxHeight", Json::JsonValue::CreateNumberValue(size.GetNamedNumber(L"height", minimum ? 100 : 20000)));
|
|
216
|
+
}
|
|
217
|
+
return style;
|
|
218
|
+
}
|
|
219
|
+
static double Dimension(Json::JsonObject const &args, wchar_t const *key, double fallback) {
|
|
158
220
|
const auto value = args.GetNamedNumber(key, fallback);
|
|
159
221
|
if (!std::isfinite(value) || value < 100 || value > 20000) throw winrt::hresult_invalid_argument(L"Invalid window dimension");
|
|
160
|
-
return
|
|
222
|
+
return value;
|
|
161
223
|
}
|
|
162
224
|
static void ApplyAppearance(Window &window) {
|
|
163
225
|
auto foreground = winrt::Windows::UI::ViewManagement::UISettings().GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
|
|
@@ -174,14 +236,18 @@ static void RestoreFrame(std::string const &id) {
|
|
|
174
236
|
auto &window = windows.at(id); if (!window.restoreFrame) return;
|
|
175
237
|
try {
|
|
176
238
|
std::ifstream stream(SparkFile(id)); std::string text((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
|
|
177
|
-
auto
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
auto
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
239
|
+
auto saved = Json::JsonObject::Parse(to_hstring(text)); if (saved.GetNamedNumber(L"version", 0) != 1) return;
|
|
240
|
+
auto bounds = saved.GetNamedObject(L"bounds"); auto displays = DisplayGeometries(); auto display = displays.front();
|
|
241
|
+
for (auto const &candidate : displays) if (candidate.primary) display = candidate;
|
|
242
|
+
auto persistent = saved.GetNamedString(L"persistentId", L"");
|
|
243
|
+
for (auto const &candidate : displays) if (!persistent.empty() && candidate.persistentId == persistent.c_str()) { display = candidate; break; }
|
|
244
|
+
auto width = std::clamp(Dimension(bounds, L"width", 640), window.minWidth, window.maxWidth), height = std::clamp(Dimension(bounds, L"height", 480), window.minHeight, window.maxHeight);
|
|
245
|
+
auto work = LogicalRect(display.work, display); const auto left = work.GetNamedNumber(L"x"), top = work.GetNamedNumber(L"y");
|
|
246
|
+
bounds.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue(width)); bounds.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue(height));
|
|
247
|
+
bounds.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue(std::clamp(bounds.GetNamedNumber(L"x"), left, left + std::max(0.0, work.GetNamedNumber(L"width") - width))));
|
|
248
|
+
bounds.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue(std::clamp(bounds.GetNamedNumber(L"y"), top, top + std::max(0.0, work.GetNamedNumber(L"height") - height))));
|
|
249
|
+
bounds.SetNamedValue(L"displayId", Json::JsonValue::CreateStringValue(display.id)); SetBounds(window, bounds);
|
|
250
|
+
} catch (...) { /* Invalid or old development data uses configured defaults. */ }
|
|
185
251
|
}
|
|
186
252
|
static void ApplyOptions(Window &window, Json::JsonObject const &options) {
|
|
187
253
|
static const std::set<std::wstring> supported{L"title", L"width", L"height", L"minWidth", L"minHeight", L"maxWidth", L"maxHeight", L"resizable", L"minimizable", L"alwaysOnTop", L"closable", L"appearance", L"backgroundColor", L"restoreFrame", L"titleBarStyle", L"transparent", L"hasShadow"};
|
|
@@ -189,9 +255,9 @@ static void ApplyOptions(Window &window, Json::JsonObject const &options) {
|
|
|
189
255
|
const auto minWidth = Dimension(options, L"minWidth", window.minWidth), minHeight = Dimension(options, L"minHeight", window.minHeight);
|
|
190
256
|
const auto maxWidth = Dimension(options, L"maxWidth", window.maxWidth), maxHeight = Dimension(options, L"maxHeight", window.maxHeight);
|
|
191
257
|
if (minWidth > maxWidth || minHeight > maxHeight) throw winrt::hresult_invalid_argument(L"Invalid window constraints");
|
|
192
|
-
auto size = window.app.Size();
|
|
193
|
-
const auto width = Dimension(options, L"width", std::clamp(size.Width, minWidth, maxWidth));
|
|
194
|
-
const auto height = Dimension(options, L"height", std::clamp(size.Height, minHeight, maxHeight));
|
|
258
|
+
auto size = window.app.Size(); const double scale = GetDpiForWindow(Hwnd(window)) / 96.0;
|
|
259
|
+
const auto width = Dimension(options, L"width", std::clamp(size.Width / scale, minWidth, maxWidth));
|
|
260
|
+
const auto height = Dimension(options, L"height", std::clamp(size.Height / scale, minHeight, maxHeight));
|
|
195
261
|
if (width < minWidth || width > maxWidth || height < minHeight || height > maxHeight) throw winrt::hresult_invalid_argument(L"Window size is outside its constraints");
|
|
196
262
|
window.minWidth = minWidth; window.minHeight = minHeight; window.maxWidth = maxWidth; window.maxHeight = maxHeight;
|
|
197
263
|
if (options.HasKey(L"titleBarStyle")) {
|
|
@@ -235,13 +301,16 @@ static void ApplyOptions(Window &window, Json::JsonObject const &options) {
|
|
|
235
301
|
}
|
|
236
302
|
window.background.Brush(compositor.CreateColorBrush(winrt::Windows::UI::Color{static_cast<uint8_t>(rgba & 255), static_cast<uint8_t>((rgba >> 24) & 255), static_cast<uint8_t>((rgba >> 16) & 255), static_cast<uint8_t>((rgba >> 8) & 255)}));
|
|
237
303
|
}
|
|
238
|
-
if (window.app.Presenter().Kind() != UI::AppWindowPresenterKind::FullScreen) window.app.Resize({width, height});
|
|
304
|
+
if (window.app.Presenter().Kind() != UI::AppWindowPresenterKind::FullScreen) window.app.Resize({static_cast<int32_t>(std::round(width * scale)), static_cast<int32_t>(std::round(height * scale))});
|
|
239
305
|
}
|
|
240
306
|
static void QueueLaunch(std::wstring const &commandLine, bool initial) {
|
|
241
307
|
int count = 0; auto argv = CommandLineToArgvW(commandLine.c_str(), &count);
|
|
242
308
|
if (!argv) return;
|
|
243
309
|
if (initial) for (int i = 0; i < count; ++i) launchArguments.Append(Json::JsonValue::CreateStringValue(argv[i]));
|
|
244
|
-
else
|
|
310
|
+
else {
|
|
311
|
+
RNW::JSValueArray arguments; for (int i = 0; i < count; ++i) arguments.push_back(to_string(argv[i]));
|
|
312
|
+
Emit({{"type", "secondInstance"}, {"arguments", std::move(arguments)}});
|
|
313
|
+
}
|
|
245
314
|
for (int i = 1; i < count; ++i) {
|
|
246
315
|
std::wstring candidate(argv[i]);
|
|
247
316
|
if (candidate.rfind(L"--spark-dock-action=", 0) == 0) {
|
|
@@ -297,15 +366,30 @@ static bool ForwardLaunch() {
|
|
|
297
366
|
static void SaveFrame(std::string const &id, HWND hwnd) {
|
|
298
367
|
if (dataDirectory.empty() || !windows.at(id).restoreFrame || IsIconic(hwnd) || IsZoomed(hwnd) || windows.at(id).fullscreen) return;
|
|
299
368
|
auto file = SparkFile(id), temporary = file; temporary += L".pending";
|
|
300
|
-
|
|
369
|
+
RECT frame{}; if (!GetWindowRect(hwnd, &frame)) return;
|
|
370
|
+
Json::JsonObject saved; saved.SetNamedValue(L"version", Json::JsonValue::CreateNumberValue(1)); saved.SetNamedValue(L"bounds", Info(id).GetNamedObject(L"bounds")); saved.SetNamedValue(L"persistentId", Json::JsonValue::CreateStringValue(OwningDisplay(frame).persistentId));
|
|
371
|
+
auto text = to_string(saved.Stringify()); std::ofstream stream(temporary, std::ios::binary | std::ios::trunc); stream << text; stream.close();
|
|
301
372
|
if (stream) MoveFileExW(temporary.c_str(), file.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
|
302
373
|
}
|
|
374
|
+
static void FinishQuit(bool allow) {
|
|
375
|
+
if (!quitPending) return;
|
|
376
|
+
quitPending = false; pendingQuitGuards.clear(); if (!allow) FinishClose("main", false);
|
|
377
|
+
if (windows.count("main")) KillTimer(Hwnd(windows.at("main")), 0x4C48);
|
|
378
|
+
auto waiters = std::move(quitWaiters); quitWaiters.clear();
|
|
379
|
+
const auto result = allow ? "{\"quitRequested\":true}" : "{\"quitRequested\":false,\"reason\":\"vetoed\"}";
|
|
380
|
+
for (auto const &waiter : waiters) waiter.Resolve(result);
|
|
381
|
+
if (allow) {
|
|
382
|
+
quitAccepted = true; Emit({{"type", "willQuit"}});
|
|
383
|
+
windows.at("main").allow = true; windows.at("main").app.Destroy();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
303
386
|
static void RequestQuit() {
|
|
304
387
|
if (quitPending) return;
|
|
305
|
-
if (!quitGuard) { windows.at("main").allow = true; windows.at("main").app.Destroy(); return; }
|
|
306
388
|
quitPending = true; auto request = quitRequest = ++generation;
|
|
307
|
-
|
|
308
|
-
|
|
389
|
+
if (quitGuards.empty()) { FinishQuit(true); return; }
|
|
390
|
+
pendingQuitGuards = quitGuards;
|
|
391
|
+
for (auto const &guard : pendingQuitGuards) Emit({{"type", "beforeQuit"}, {"guardId", guard}, {"requestId", static_cast<int64_t>(request)}});
|
|
392
|
+
if (!SetTimer(Hwnd(windows.at("main")), 0x4C48, 30000, nullptr)) FinishQuit(false);
|
|
309
393
|
}
|
|
310
394
|
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
|
|
311
395
|
auto id = Id(hwnd);
|
|
@@ -320,13 +404,16 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
|
|
320
404
|
auto text = static_cast<wchar_t const *>(notification->lpData); if (text[notification->cbData / sizeof(wchar_t) - 1]) return FALSE;
|
|
321
405
|
try { QueueNotificationResponse(to_string(text)); return TRUE; } catch (...) { return FALSE; }
|
|
322
406
|
}
|
|
323
|
-
auto data = reinterpret_cast<COPYDATASTRUCT *>(lParam); if (data && data->dwData == 0x4C47 && data->cbData >= sizeof(wchar_t) && data->cbData <= 65536 && data->cbData % sizeof(wchar_t) == 0) { auto text = static_cast<wchar_t *>(data->lpData); if (text && text[data->cbData / sizeof(wchar_t) - 1] == 0) { QueueLaunch(text, false);
|
|
407
|
+
auto data = reinterpret_cast<COPYDATASTRUCT *>(lParam); if (data && data->dwData == 0x4C47 && data->cbData >= sizeof(wchar_t) && data->cbData <= 65536 && data->cbData % sizeof(wchar_t) == 0) { auto text = static_cast<wchar_t *>(data->lpData); if (text && text[data->cbData / sizeof(wchar_t) - 1] == 0) { QueueLaunch(text, false);
|
|
408
|
+
bool visible = false; for (auto const &[key, window] : windows) if (IsWindowVisible(Hwnd(window)) && !window.overlay) visible = true;
|
|
409
|
+
Emit({{"type", "reopen"}, {"hasVisibleWindows", visible}, {"mainWindowWasVisible", !!IsWindowVisible(Hwnd(windows.at("main")))}}); ActivateApplication(); return TRUE; } } return FALSE; }
|
|
324
410
|
if (message == WM_GETMINMAXINFO) {
|
|
325
411
|
auto limits = reinterpret_cast<MINMAXINFO *>(lParam); auto const &window = windows.at(id);
|
|
326
|
-
|
|
412
|
+
const double scale = GetDpiForWindow(hwnd) / 96.0;
|
|
413
|
+
limits->ptMinTrackSize = {static_cast<LONG>(std::ceil(window.minWidth * scale)), static_cast<LONG>(std::ceil(window.minHeight * scale))}; limits->ptMaxTrackSize = {static_cast<LONG>(std::floor(window.maxWidth * scale)), static_cast<LONG>(std::floor(window.maxHeight * scale))}; return 0;
|
|
327
414
|
}
|
|
328
415
|
if (message == WM_SHOWWINDOW) PostMessageW(hwnd, WM_APP + 18, 0, 0);
|
|
329
|
-
if (message == WM_APP + 18) { UpdateModality(); return 0; }
|
|
416
|
+
if (message == WM_APP + 18) { UpdateModality(); Emit({{"type", "visibilityChanged"}, {"windowId", id}}); return 0; }
|
|
330
417
|
if (message == WM_SETTINGCHANGE || message == WM_THEMECHANGED) ApplyAppearance(windows.at(id));
|
|
331
418
|
if (message == WM_SYSCOMMAND && (wParam & 0xfff0) == SC_CLOSE && !windows.at(id).closable) return 0;
|
|
332
419
|
if (message == WM_EXITSIZEMOVE) SaveFrame(id, hwnd);
|
|
@@ -347,12 +434,13 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
|
|
|
347
434
|
if (message == WM_COMMAND && (HIWORD(wParam) == 0 || HIWORD(wParam) == 1)) {
|
|
348
435
|
auto found = menuActions.find(LOWORD(wParam)); if (found != menuActions.end() && context) { context.EmitJSEvent(L"RCTDeviceEventEmitter", L"NativeMenuAction", found->second.Copy()); return 0; }
|
|
349
436
|
}
|
|
350
|
-
if (message == WM_TIMER && wParam ==
|
|
437
|
+
if (message == WM_TIMER && wParam == 0x4C48) { FinishQuit(false); return 0; }
|
|
438
|
+
if (message == WM_TIMER && wParam == 0x4C47) { windows.at(id).pending = false; KillTimer(hwnd, 0x4C47); FinishClose(id, false); Emit({{"type", "closeGuardTimeout"}, {"windowId", id}}); return 0; }
|
|
351
439
|
}
|
|
352
440
|
return DefSubclassProc(hwnd, message, wParam, lParam);
|
|
353
441
|
}
|
|
354
442
|
static void Attach(std::string id, RNW::ReactNativeWindow view) {
|
|
355
|
-
Window window; window.view = view; window.app = view.AppWindow(); window.normal = window.app.Presenter().as<UI::OverlappedPresenter>(); window.monitor = MonitorFromWindow(Hwnd(window), MONITOR_DEFAULTTONEAREST); windows.emplace(id, window);
|
|
443
|
+
Window window; window.instanceId = std::to_string(++generation); window.view = view; window.app = view.AppWindow(); window.normal = window.app.Presenter().as<UI::OverlappedPresenter>(); window.monitor = MonitorFromWindow(Hwnd(window), MONITOR_DEFAULTTONEAREST); windows.emplace(id, window);
|
|
356
444
|
SetPropW(Hwnd(window), (L"Spark.Window." + std::wstring(to_hstring(id))).c_str(), reinterpret_cast<HANDLE>(1));
|
|
357
445
|
SetPropW(Hwnd(window), L"Spark.ReactWindow", reinterpret_cast<HANDLE>(1));
|
|
358
446
|
if (id == "main") SetNotificationWindow(Hwnd(window));
|
|
@@ -366,11 +454,13 @@ static void Attach(std::string id, RNW::ReactNativeWindow view) {
|
|
|
366
454
|
if (window.guard) {
|
|
367
455
|
args.Cancel(true); if (window.pending) return;
|
|
368
456
|
window.pending = true; window.request = ++generation;
|
|
369
|
-
SetTimer(Hwnd(window), 0x4C47, 30000, nullptr);
|
|
457
|
+
if (!SetTimer(Hwnd(window), 0x4C47, 30000, nullptr)) { window.pending = false; FinishClose(id, false); return; }
|
|
370
458
|
Emit({{"type", "beforeClose"}, {"windowId", id}, {"requestId", static_cast<int64_t>(window.request)}});
|
|
371
|
-
} else if (id == "main" &&
|
|
459
|
+
} else if (id == "main" && !quitGuards.empty()) { args.Cancel(true); RequestQuit(); }
|
|
372
460
|
});
|
|
373
461
|
app.Destroying([id](auto const &, auto const &) {
|
|
462
|
+
FinishClose(id, true);
|
|
463
|
+
if (id == "main" && !quitAccepted) { quitAccepted = true; Emit({{"type", "willQuit"}}); }
|
|
374
464
|
Emit({{"type", "closed"}, {"windowId", id}});
|
|
375
465
|
if (id != "main") {
|
|
376
466
|
// A menu attached to a destroyed HWND is otherwise destroyed by Windows.
|
|
@@ -397,9 +487,13 @@ static LRESULT CALLBACK Keyboard(int code, WPARAM remove, LPARAM param) {
|
|
|
397
487
|
if (GetKeyState(VK_MENU) < 0) modifiers |= 1 << 19;
|
|
398
488
|
if (GetKeyState(VK_SHIFT) < 0) modifiers |= 1 << 17;
|
|
399
489
|
if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0) modifiers |= 1 << 20;
|
|
490
|
+
Shortcut const *match = nullptr;
|
|
400
491
|
for (auto const &[key, shortcut] : shortcuts) if (!id.empty() && (shortcut.windowId.empty() || shortcut.windowId == id) && shortcut.key == message->wParam && shortcut.modifiers == modifiers) {
|
|
401
|
-
if (
|
|
402
|
-
|
|
492
|
+
if (!match || !shortcut.windowId.empty()) match = &shortcut;
|
|
493
|
+
}
|
|
494
|
+
if (match) {
|
|
495
|
+
if (match->repeat || !(message->lParam & (1LL << 30))) context.EmitJSEvent(L"RCTDeviceEventEmitter", L"shortcut", RNW::JSValueObject{{"id", match->id}});
|
|
496
|
+
message->message = WM_NULL;
|
|
403
497
|
}
|
|
404
498
|
}
|
|
405
499
|
}
|
|
@@ -418,15 +512,21 @@ static RNW::JSValue Value(Json::IJsonValue const &value) {
|
|
|
418
512
|
static std::wstring MenuTitle(winrt::hstring const &title) {
|
|
419
513
|
std::wstring result; for (auto c : title) { result += c; if (c == L'&') result += c; } return result;
|
|
420
514
|
}
|
|
421
|
-
static
|
|
422
|
-
auto
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
for (auto const &entry : menu.GetNamedArray(L"items")) {
|
|
515
|
+
static HMENU BuildMenuItems(Json::JsonArray const &items, std::map<UINT, RNW::JSValueObject> &actions, std::vector<ACCEL> &accelerators, UINT &command, bool parentEnabled, bool root = false) {
|
|
516
|
+
auto popup = root ? CreateMenu() : CreatePopupMenu(); if (!popup) throw winrt::hresult_error(E_FAIL, L"Could not allocate application menu");
|
|
517
|
+
try {
|
|
518
|
+
for (auto const &entry : items) {
|
|
426
519
|
auto item = entry.GetObject(); if (item.GetNamedBoolean(L"hidden", false)) continue;
|
|
427
|
-
if (item.GetNamedBoolean(L"separator", false)) { AppendMenuW(popup, MF_SEPARATOR, 0, nullptr); continue; }
|
|
428
|
-
|
|
520
|
+
if (item.GetNamedBoolean(L"separator", false)) { if (!AppendMenuW(popup, MF_SEPARATOR, 0, nullptr)) throw winrt::hresult_error(E_FAIL, L"Could not add separator"); continue; }
|
|
521
|
+
const bool enabled = parentEnabled && item.GetNamedBoolean(L"enabled", true);
|
|
522
|
+
auto flags = MF_STRING | (enabled ? MF_ENABLED : MF_GRAYED) | (item.GetNamedBoolean(L"checked", false) ? MF_CHECKED : 0);
|
|
429
523
|
auto title = MenuTitle(item.GetNamedString(L"title", L""));
|
|
524
|
+
if (item.HasKey(L"items")) {
|
|
525
|
+
auto child = BuildMenuItems(item.GetNamedArray(L"items"), actions, accelerators, command, enabled);
|
|
526
|
+
if (!AppendMenuW(popup, flags | MF_POPUP, reinterpret_cast<UINT_PTR>(child), title.c_str())) { DestroyMenu(child); throw winrt::hresult_error(E_FAIL, L"Could not add submenu"); }
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (command > 0xffff) throw winrt::hresult_invalid_argument(L"Too many application menu actions");
|
|
430
530
|
if (item.HasKey(L"shortcut") && item.GetNamedValue(L"shortcut").ValueType() == Json::JsonValueType::Object) {
|
|
431
531
|
auto shortcut = item.GetNamedObject(L"shortcut"); auto key = shortcut.GetNamedString(L"key", L"");
|
|
432
532
|
if (key.size() == 1 && command <= 0xffff) {
|
|
@@ -439,23 +539,39 @@ static void RebuildMenus() {
|
|
|
439
539
|
else if (c == 0xf728) code = VK_DELETE; else if (c == 0x7f) code = VK_BACK;
|
|
440
540
|
else { auto scan = VkKeyScanW(c); if (scan != -1) { code = LOBYTE(scan); if (HIBYTE(scan) & 1) modifiers |= FSHIFT; if (HIBYTE(scan) & 2) modifiers |= FCONTROL; if (HIBYTE(scan) & 4) modifiers |= FALT; } }
|
|
441
541
|
if (code) {
|
|
442
|
-
if (
|
|
542
|
+
if (enabled) accelerators.push_back({modifiers, code, static_cast<WORD>(command)});
|
|
443
543
|
title += L"\t"; if (modifiers & FCONTROL) title += L"Ctrl+"; if (modifiers & FALT) title += L"Alt+"; if (modifiers & FSHIFT) title += L"Shift+";
|
|
444
544
|
wchar_t label[80]{}; auto scan = MapVirtualKeyW(code, MAPVK_VK_TO_VSC); if (code == VK_LEFT || code == VK_RIGHT || code == VK_UP || code == VK_DOWN || code == VK_DELETE) scan |= 0x100;
|
|
445
545
|
GetKeyNameTextW(static_cast<LONG>(scan << 16), label, 80); title += label;
|
|
446
546
|
}
|
|
447
547
|
}
|
|
448
548
|
}
|
|
449
|
-
AppendMenuW(popup, flags, command, title.c_str());
|
|
450
|
-
actions.emplace(command
|
|
549
|
+
if (!AppendMenuW(popup, flags, command, title.c_str())) throw winrt::hresult_error(E_FAIL, L"Could not add menu action");
|
|
550
|
+
if (enabled) actions.emplace(command, RNW::JSValueObject{{"ownerId", to_string(item.GetNamedString(L"_sparkOwner"))}, {"itemId", to_string(item.GetNamedString(L"id"))}});
|
|
551
|
+
command++;
|
|
451
552
|
}
|
|
452
|
-
|
|
553
|
+
return popup;
|
|
554
|
+
} catch (...) { DestroyMenu(popup); throw; }
|
|
555
|
+
}
|
|
556
|
+
static void RebuildMenus(Json::JsonArray const &configuration) {
|
|
557
|
+
if (configuration.Size() == 0) {
|
|
558
|
+
for (auto const &[id, window] : windows) if (currentMenu && GetMenu(Hwnd(window)) == currentMenu) { SetMenu(Hwnd(window), nullptr); DrawMenuBar(Hwnd(window)); }
|
|
559
|
+
if (currentMenu) DestroyMenu(currentMenu); currentMenu = nullptr; menuActions.clear();
|
|
560
|
+
if (acceleratorTable) DestroyAcceleratorTable(acceleratorTable); acceleratorTable = nullptr;
|
|
561
|
+
return;
|
|
453
562
|
}
|
|
454
|
-
|
|
455
|
-
auto
|
|
563
|
+
std::map<UINT, RNW::JSValueObject> actions; std::vector<ACCEL> accelerators; UINT command = 0x5000;
|
|
564
|
+
auto next = BuildMenuItems(configuration, actions, accelerators, command, true, true);
|
|
565
|
+
HACCEL keys = nullptr;
|
|
566
|
+
try {
|
|
567
|
+
if (!accelerators.empty()) { keys = CreateAcceleratorTableW(accelerators.data(), static_cast<int>(accelerators.size())); if (!keys) throw winrt::hresult_error(E_FAIL, L"Could not create menu accelerators"); }
|
|
568
|
+
auto target = GetForegroundWindow(); if (Id(target).empty()) { auto main = windows.find("main"); if (main == windows.end()) throw winrt::hresult_error(E_FAIL, L"Main window is unavailable"); target = Hwnd(main->second); }
|
|
569
|
+
if (!SetMenu(target, next)) throw winrt::hresult_error(E_FAIL, L"Could not attach application menu");
|
|
570
|
+
for (auto const &[id, window] : windows) if (Hwnd(window) != target && GetMenu(Hwnd(window)) == currentMenu) SetMenu(Hwnd(window), nullptr);
|
|
571
|
+
DrawMenuBar(target);
|
|
572
|
+
} catch (...) { if (keys) DestroyAcceleratorTable(keys); DestroyMenu(next); throw; }
|
|
456
573
|
auto old = currentMenu; currentMenu = next; menuActions = std::move(actions); if (old) DestroyMenu(old);
|
|
457
|
-
if (acceleratorTable) DestroyAcceleratorTable(acceleratorTable);
|
|
458
|
-
acceleratorTable = accelerators.empty() ? nullptr : CreateAcceleratorTableW(accelerators.data(), static_cast<int>(accelerators.size()));
|
|
574
|
+
if (acceleratorTable) DestroyAcceleratorTable(acceleratorTable); acceleratorTable = keys;
|
|
459
575
|
}
|
|
460
576
|
static void Initialize(RNW::ReactNativeWin32App const &app) {
|
|
461
577
|
// WinRT activation must follow init_apartment, never static initialization.
|
|
@@ -489,7 +605,7 @@ static void Initialize(RNW::ReactNativeWin32App const &app) {
|
|
|
489
605
|
REACT_MODULE(SparkDesktopApp, L"NativeDesktopApp")
|
|
490
606
|
struct SparkDesktopApp {
|
|
491
607
|
REACT_INIT(Initialize)
|
|
492
|
-
void Initialize(winrt::Microsoft::ReactNative::ReactContext const &context) noexcept { if (context.Properties().Get(winrt::Microsoft::ReactNative::ReactPropertyId<bool>{L"Spark", L"SecondaryRuntime"}).value_or(false)) return; SparkWin::context = context; SparkWin::
|
|
608
|
+
void Initialize(winrt::Microsoft::ReactNative::ReactContext const &context) noexcept { if (context.Properties().Get(winrt::Microsoft::ReactNative::ReactPropertyId<bool>{L"Spark", L"SecondaryRuntime"}).value_or(false)) return; SparkWin::context = context; SparkWin::quitGuards.clear(); SparkWin::pendingQuitGuards.clear(); SparkWin::quitWaiters.clear(); SparkWin::quitPending = false; SparkWin::quitAccepted = false; SparkWin::shortcuts.clear(); for (auto &[id, window] : SparkWin::windows) { window.guard = false; window.pending = false; window.guardId.clear(); window.closeWaiters.clear(); KillTimer(SparkWin::Hwnd(window), 0x4C47); } }
|
|
493
609
|
REACT_METHOD(addListener) void addListener(std::string) noexcept {}
|
|
494
610
|
REACT_METHOD(removeListeners) void removeListeners(double) noexcept {}
|
|
495
611
|
REACT_METHOD(call)
|
|
@@ -499,10 +615,18 @@ struct SparkDesktopApp {
|
|
|
499
615
|
try {
|
|
500
616
|
auto args = Json::JsonObject::Parse(to_hstring(encoded));
|
|
501
617
|
if (method == "notificationResponses") { promise.Resolve(NotificationResponses()); return; }
|
|
502
|
-
if (method == "quitGuard")
|
|
503
|
-
|
|
618
|
+
if (method == "quitGuard") {
|
|
619
|
+
const auto id = to_string(args.GetNamedString(L"id"));
|
|
620
|
+
if (id.empty()) throw winrt::hresult_invalid_argument(L"Expected quit guard ID");
|
|
621
|
+
if (args.GetNamedBoolean(L"enabled")) quitGuards.insert(id); else quitGuards.erase(id);
|
|
622
|
+
FinishQuit(false);
|
|
623
|
+
} else if (method == "quit") { quitWaiters.push_back(promise); RequestQuit(); return; }
|
|
504
624
|
else if (method == "replyQuit") {
|
|
505
|
-
|
|
625
|
+
auto id = to_string(args.GetNamedString(L"id"));
|
|
626
|
+
if (quitPending && args.GetNamedNumber(L"requestId") == quitRequest && pendingQuitGuards.erase(id)) {
|
|
627
|
+
if (!args.GetNamedBoolean(L"allow")) FinishQuit(false);
|
|
628
|
+
else if (pendingQuitGuards.empty()) FinishQuit(true);
|
|
629
|
+
}
|
|
506
630
|
} else if (method == "hide") HideApplication();
|
|
507
631
|
else if (method == "activate") ActivateApplication();
|
|
508
632
|
else if (method == "context") {
|
|
@@ -522,13 +646,12 @@ struct SparkDesktopWindows {
|
|
|
522
646
|
void call(std::string method, std::string encoded, winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept {
|
|
523
647
|
SparkWin::context.UIDispatcher().Post([method, encoded, promise]() {
|
|
524
648
|
using namespace SparkWin;
|
|
649
|
+
std::string candidateId;
|
|
525
650
|
try {
|
|
526
651
|
auto args = Json::JsonObject::Parse(to_hstring(encoded)); auto id = to_string(args.GetNamedString(L"id", L"main"));
|
|
527
652
|
if (method == "open") {
|
|
528
|
-
if (
|
|
529
|
-
|
|
530
|
-
promise.Resolve(to_string(Info(id).Stringify())); return;
|
|
531
|
-
}
|
|
653
|
+
if (id == "main") { promise.Reject(RNW::ReactError{"E_INVALID_ARGUMENT", "Main window is host-created"}); return; }
|
|
654
|
+
if (windows.count(id)) { promise.Reject(RNW::ReactError{"E_ALREADY_EXISTS", "Window ID is already open"}); return; }
|
|
532
655
|
const auto parentId = to_string(args.GetNamedString(L"parentId", L""));
|
|
533
656
|
const auto kind = args.GetNamedString(L"kind", L"window");
|
|
534
657
|
if (kind != L"window" && kind != L"overlay") throw winrt::hresult_invalid_argument(L"Invalid window kind");
|
|
@@ -540,64 +663,91 @@ struct SparkDesktopWindows {
|
|
|
540
663
|
if (modal) for (auto const &[childId, child] : windows) if (child.parentId == parentId && child.modal) { promise.Reject(RNW::ReactError{"E_BUSY", "Parent already has a modal window"}); return; }
|
|
541
664
|
auto view = RNW::ReactNativeWindow::CreateFromCompositor(compositor); auto app = view.AppWindow();
|
|
542
665
|
app.Title(args.GetNamedString(L"title", SparkWindowsEnv(L"SPARK_PROJECT_NAME"))); app.Resize({static_cast<int32_t>(args.GetNamedNumber(L"width", 640)), static_cast<int32_t>(args.GetNamedNumber(L"height", 480))});
|
|
543
|
-
RNW::ReactViewOptions options; options.ComponentName(L"
|
|
544
|
-
|
|
666
|
+
RNW::ReactViewOptions options; options.ComponentName(args.GetNamedString(L"component"));
|
|
667
|
+
auto props = args.GetNamedObject(L"props", Json::JsonObject());
|
|
545
668
|
// Write JSON props through the public JSValue writer contract.
|
|
546
669
|
auto propsJson = to_string(props.Stringify());
|
|
547
670
|
options.InitialProps([propsJson](RNW::IJSValueWriter const &writer) { WriteJson(writer, Json::JsonObject::Parse(to_hstring(propsJson))); });
|
|
548
671
|
// Validate all options before attaching/showing a new React root.
|
|
549
|
-
|
|
550
|
-
for (auto const &pair : args) if (pair.Key() != L"id" && pair.Key() != L"props" && pair.Key() != L"parentId" && pair.Key() != L"modal" && pair.Key() != L"kind") style.SetNamedValue(pair.Key(), pair.Value());
|
|
672
|
+
auto style = CanonicalStyle(args);
|
|
551
673
|
Window pending; pending.overlay = overlay; pending.view = view; pending.app = app; pending.normal = app.Presenter().as<UI::OverlappedPresenter>();
|
|
552
674
|
try { ApplyOptions(pending, style); } catch (...) { app.Destroy(); throw; }
|
|
553
675
|
if (overlay) {
|
|
554
676
|
auto hwnd = Hwnd(pending);
|
|
555
677
|
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW);
|
|
556
678
|
}
|
|
557
|
-
Attach(id, view);
|
|
679
|
+
Attach(id, view); candidateId = id;
|
|
558
680
|
auto &attached = windows.at(id); attached.parentId = parentId; attached.modal = modal; attached.overlay = overlay; attached.transparent = pending.transparent; attached.shadow = pending.shadow;
|
|
559
681
|
if (!parentId.empty()) {
|
|
560
682
|
SetLastError(0); SetWindowLongPtrW(Hwnd(attached), GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(Hwnd(windows.at(parentId))));
|
|
561
683
|
if (GetLastError()) { app.Destroy(); throw winrt::hresult_error(HRESULT_FROM_WIN32(GetLastError())); }
|
|
562
684
|
}
|
|
563
|
-
attached.minWidth = pending.minWidth; attached.minHeight = pending.minHeight; attached.maxWidth = pending.maxWidth; attached.maxHeight = pending.maxHeight; attached.closable = pending.closable; attached.restoreFrame =
|
|
564
|
-
view.ReactNativeIsland().ReactViewHost(RNW::ReactCoreInjection::MakeViewHost(host, options));
|
|
685
|
+
attached.minWidth = pending.minWidth; attached.minHeight = pending.minHeight; attached.maxWidth = pending.maxWidth; attached.maxHeight = pending.maxHeight; attached.closable = pending.closable; attached.restoreFrame = args.GetNamedBoolean(L"restoreBounds", false); attached.theme = pending.theme; attached.resources = pending.resources; attached.background = pending.background;
|
|
686
|
+
view.ReactNativeIsland().ReactViewHost(RNW::ReactCoreInjection::MakeViewHost(host, options));
|
|
687
|
+
auto displays = DisplayGeometries(); auto display = displays.front();
|
|
688
|
+
for (auto const &candidate : displays) if (candidate.primary) display = candidate;
|
|
689
|
+
if (!parentId.empty()) { RECT parentFrame{}; GetWindowRect(Hwnd(windows.at(parentId)), &parentFrame); display = OwningDisplay(parentFrame); }
|
|
690
|
+
if (args.HasKey(L"position")) display = DisplayWithId(args.GetNamedObject(L"position").GetNamedString(L"displayId"));
|
|
691
|
+
auto size = args.GetNamedObject(L"size", Json::JsonObject()); double width = std::clamp(size.GetNamedNumber(L"width", 640), attached.minWidth, attached.maxWidth), height = std::clamp(size.GetNamedNumber(L"height", 480), attached.minHeight, attached.maxHeight);
|
|
692
|
+
auto work = LogicalRect(display.work, display); Json::JsonObject bounds;
|
|
693
|
+
bounds.SetNamedValue(L"displayId", Json::JsonValue::CreateStringValue(display.id)); bounds.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue(width)); bounds.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue(height));
|
|
694
|
+
bounds.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue(work.GetNamedNumber(L"x") + (work.GetNamedNumber(L"width") - width) / 2)); bounds.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue(work.GetNamedNumber(L"y") + (work.GetNamedNumber(L"height") - height) / 2));
|
|
695
|
+
SetBounds(attached, bounds); RestoreFrame(id); bounds = Info(id).GetNamedObject(L"bounds");
|
|
696
|
+
if (args.HasKey(L"size")) { bounds.SetNamedValue(L"width", Json::JsonValue::CreateNumberValue(width)); bounds.SetNamedValue(L"height", Json::JsonValue::CreateNumberValue(height)); }
|
|
697
|
+
if (args.HasKey(L"position")) for (auto const &entry : args.GetNamedObject(L"position")) bounds.SetNamedValue(entry.Key(), entry.Value());
|
|
698
|
+
SetBounds(attached, bounds); ApplyAppearance(attached); if (args.GetNamedBoolean(L"show", true)) app.Show(!overlay); UpdateModality(); Emit({{"type", "opened"}, {"windowId", id}});
|
|
565
699
|
promise.Resolve(to_string(Info(id).Stringify())); return;
|
|
566
700
|
}
|
|
567
701
|
if (method == "displays") { promise.Resolve(to_string(Displays().Stringify())); return; }
|
|
702
|
+
if (method == "cursorPoint") {
|
|
703
|
+
POINT cursor{}; if (!GetCursorPos(&cursor)) winrt::throw_last_error();
|
|
704
|
+
RECT probe{cursor.x, cursor.y, cursor.x + 1, cursor.y + 1};
|
|
705
|
+
auto display = OwningDisplay(probe);
|
|
706
|
+
auto point = Json::JsonObject();
|
|
707
|
+
point.SetNamedValue(L"displayId", Json::JsonValue::CreateStringValue(display.id));
|
|
708
|
+
point.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue((cursor.x - display.frame.left) / display.scale));
|
|
709
|
+
point.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue((cursor.y - display.frame.top) / display.scale));
|
|
710
|
+
promise.Resolve(to_string(point.Stringify())); return;
|
|
711
|
+
}
|
|
568
712
|
if (method == "list") { Json::JsonArray result; for (auto const &[name, window] : windows) result.Append(Info(name)); promise.Resolve(to_string(result.Stringify())); return; }
|
|
569
713
|
auto found = windows.find(id); if (found == windows.end()) { promise.Reject(RNW::ReactError{"E_NOT_FOUND", "Window not found"}); return; }
|
|
570
714
|
auto &window = found->second; auto hwnd = Hwnd(window);
|
|
571
|
-
if (
|
|
572
|
-
if (method == "
|
|
715
|
+
if (args.HasKey(L"instanceId") && to_string(args.GetNamedString(L"instanceId")) != window.instanceId) { promise.Reject(RNW::ReactError{"E_NOT_FOUND", "Window instance has closed"}); return; }
|
|
716
|
+
if (method == "info" || method == "observe") { promise.Resolve(to_string(Info(id).Stringify())); return; }
|
|
717
|
+
if (method == "options") ApplyOptions(window, CanonicalStyle(args.GetNamedObject(L"options")));
|
|
573
718
|
else if (method == "center") {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
auto
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
auto width = Dimension(frame, L"width", window.app.Size().Width), height = Dimension(frame, L"height", window.app.Size().Height);
|
|
582
|
-
if (width < window.minWidth || width > window.maxWidth || height < window.minHeight || height > window.maxHeight) throw winrt::hresult_invalid_argument(L"Frame is outside window constraints");
|
|
583
|
-
window.app.MoveAndResize({static_cast<int32_t>(x), static_cast<int32_t>(y), width, height});
|
|
719
|
+
RECT frame{}; if (!GetWindowRect(hwnd, &frame)) winrt::throw_last_error();
|
|
720
|
+
auto display = args.HasKey(L"displayId") ? DisplayWithId(args.GetNamedString(L"displayId")) : OwningDisplay(frame);
|
|
721
|
+
auto bounds = Info(id).GetNamedObject(L"bounds"), work = LogicalRect(display.work, display);
|
|
722
|
+
bounds.SetNamedValue(L"displayId", Json::JsonValue::CreateStringValue(display.id));
|
|
723
|
+
bounds.SetNamedValue(L"x", Json::JsonValue::CreateNumberValue(work.GetNamedNumber(L"x") + (work.GetNamedNumber(L"width") - bounds.GetNamedNumber(L"width")) / 2));
|
|
724
|
+
bounds.SetNamedValue(L"y", Json::JsonValue::CreateNumberValue(work.GetNamedNumber(L"y") + (work.GetNamedNumber(L"height") - bounds.GetNamedNumber(L"height")) / 2)); SetBounds(window, bounds);
|
|
725
|
+
} else if (method == "bounds") { SetBounds(window, args.GetNamedObject(L"bounds"));
|
|
584
726
|
} else if (method == "fullscreen") {
|
|
585
727
|
if (args.GetNamedBoolean(L"enabled")) window.app.SetPresenter(UI::AppWindowPresenterKind::FullScreen);
|
|
586
728
|
else window.app.SetPresenter(window.normal);
|
|
587
729
|
WindowTransitions(id);
|
|
588
|
-
} else if (method == "
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (
|
|
592
|
-
} else if (method == "
|
|
593
|
-
|
|
730
|
+
} else if (method == "closeGuard") {
|
|
731
|
+
auto guardId = to_string(args.GetNamedString(L"guardId")); bool enabled = args.GetNamedBoolean(L"enabled");
|
|
732
|
+
if (enabled && window.guard && window.guardId != guardId) { promise.Reject(RNW::ReactError{"E_BUSY", "Window already has a close guard"}); return; }
|
|
733
|
+
if (enabled || window.guardId == guardId) { window.guard = enabled; window.guardId = enabled ? guardId : ""; window.pending = false; window.request = ++generation; KillTimer(hwnd, 0x4C47); FinishClose(id, false); }
|
|
734
|
+
} else if (method == "replyClose") {
|
|
735
|
+
if (window.pending && args.GetNamedNumber(L"requestId", 0) == window.request && to_string(args.GetNamedString(L"guardId")) == window.guardId) {
|
|
736
|
+
window.pending = false; KillTimer(hwnd, 0x4C47);
|
|
737
|
+
if (args.GetNamedBoolean(L"allow")) { if (id == "main") RequestQuit(); else { window.allow = true; window.app.Destroy(); } } else FinishClose(id, false);
|
|
738
|
+
}
|
|
739
|
+
} else if (method == "close") {
|
|
740
|
+
window.closeWaiters.push_back(promise); if (!window.pending) SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
|
741
|
+
auto stillOpen = windows.find(id); if (stillOpen != windows.end() && !stillOpen->second.pending && !(id == "main" && quitPending)) FinishClose(id, false);
|
|
742
|
+
return;
|
|
743
|
+
} else if (method == "show") { const bool focus = args.GetNamedBoolean(L"focus", true) && !window.overlay; ShowWindow(hwnd, !focus ? SW_SHOWNOACTIVATE : IsIconic(hwnd) ? SW_RESTORE : SW_SHOW); UpdateModality(); if (focus) SetForegroundWindow(hwnd); }
|
|
594
744
|
else if (method == "hide") { window.app.Hide(); UpdateModality(); }
|
|
595
745
|
else if (method == "minimize") ShowWindow(hwnd, SW_MINIMIZE);
|
|
596
746
|
else if (method == "maximize") ShowWindow(hwnd, SW_MAXIMIZE);
|
|
597
747
|
else if (method == "unmaximize") ShowWindow(hwnd, SW_RESTORE);
|
|
598
748
|
else throw winrt::hresult_invalid_argument(L"Unsupported window operation");
|
|
599
749
|
promise.Resolve("null");
|
|
600
|
-
} catch (winrt::hresult_error const &error) { promise.Reject(RNW::ReactError{"
|
|
750
|
+
} catch (winrt::hresult_error const &error) { if (!candidateId.empty() && windows.count(candidateId)) windows.at(candidateId).app.Destroy(); promise.Reject(RNW::ReactError{error.code() == E_INVALIDARG ? "E_INVALID_ARGUMENT" : error.code() == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) ? "E_NOT_FOUND" : "E_NATIVE", to_string(error.message())}); }
|
|
601
751
|
});
|
|
602
752
|
}
|
|
603
753
|
};
|
|
@@ -615,7 +765,12 @@ struct SparkDesktopShortcuts {
|
|
|
615
765
|
else if (method == "register") {
|
|
616
766
|
auto text = args.GetNamedString(L"key"); if (text.size() != 1) throw winrt::hresult_invalid_argument(L"Invalid shortcut");
|
|
617
767
|
wchar_t key = text[0]; UINT code = key >= 0xf704 && key <= 0xf717 ? VK_F1 + key - 0xf704 : key == 0xf700 ? VK_UP : key == 0xf701 ? VK_DOWN : key == 0xf702 ? VK_LEFT : key == 0xf703 ? VK_RIGHT : key == 0xf728 ? VK_DELETE : key == 0x7f ? VK_BACK : static_cast<UINT>(towupper(key));
|
|
618
|
-
|
|
768
|
+
auto windowId = to_string(args.GetNamedString(L"windowId", L""));
|
|
769
|
+
auto modifiers = static_cast<int>(args.GetNamedNumber(L"modifiers"));
|
|
770
|
+
for (auto const &[existingId, existing] : shortcuts) if (existing.key == code && existing.modifiers == modifiers && existing.windowId == windowId) {
|
|
771
|
+
promise.Reject(RNW::ReactError{"E_BUSY", "Shortcut already registered in this scope"}); return;
|
|
772
|
+
}
|
|
773
|
+
shortcuts[id] = {id, windowId, code, modifiers, args.GetNamedBoolean(L"repeat", false)};
|
|
619
774
|
} else throw winrt::hresult_invalid_argument(L"Unknown shortcut operation"); promise.Resolve("null");
|
|
620
775
|
} catch (winrt::hresult_error const &error) { promise.Reject(RNW::ReactError{"E_SHORTCUT", to_string(error.message())}); }
|
|
621
776
|
});
|
|
@@ -626,14 +781,15 @@ struct SparkNativeMenu {
|
|
|
626
781
|
REACT_INIT(Initialize) void Initialize(winrt::Microsoft::ReactNative::ReactContext const &context) noexcept { if (context.Properties().Get(winrt::Microsoft::ReactNative::ReactPropertyId<bool>{L"Spark", L"SecondaryRuntime"}).value_or(false)) return; SparkWin::context = context; }
|
|
627
782
|
REACT_METHOD(addListener) void addListener(std::string) noexcept {}
|
|
628
783
|
REACT_METHOD(removeListeners) void removeListeners(double) noexcept {}
|
|
629
|
-
REACT_METHOD(
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
784
|
+
REACT_METHOD(publish) void publish(std::string json, RNW::ReactPromise<void> promise) noexcept {
|
|
785
|
+
SparkWin::context.UIDispatcher().Post([json, promise]() {
|
|
786
|
+
try {
|
|
787
|
+
auto next = SparkWin::Json::JsonArray::Parse(winrt::to_hstring(json));
|
|
788
|
+
SparkWin::RebuildMenus(next); SparkWin::menus = next; promise.Resolve();
|
|
789
|
+
} catch (winrt::hresult_error const &error) { promise.Reject(RNW::ReactError{"E_NATIVE", winrt::to_string(error.message())}); }
|
|
790
|
+
catch (std::exception const &error) { promise.Reject(RNW::ReactError{"E_NATIVE", error.what()}); }
|
|
791
|
+
});
|
|
792
|
+
}
|
|
637
793
|
};
|
|
638
794
|
|
|
639
795
|
// Development clients never update themselves. Match the macOS development API
|