From c52370113d8b5d700b09af68e53db3aa2efd4b5b Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 23 Oct 2025 16:12:56 +0200 Subject: [PATCH] [macOS] Implement regular window (#176361) *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* *List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.* *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Matthew Kosarek --- .ci.yaml | 10 + TESTOWNERS | 1 + .../bin/tasks/windowing_test_macos.dart | 12 + .../windowing_test/lib/main.dart | 9 + .../windowing_test/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../windowing_test/macos/Podfile | 39 ++ .../macos/Runner.xcodeproj/project.pbxproj | 628 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../macos/Runner/AppDelegate.swift | 20 + .../Runner/Assets.xcassets/Contents.json | 6 + .../macos/Runner/Base.lproj/MainMenu.xib | 332 +++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../windowing_test/macos/Runner/Info.plist | 32 + .../macos/Runner/Release.entitlements | 8 + .../windowing_test/test_driver/main_test.dart | 96 +-- .../Source/FlutterWindowController.h | 31 +- .../Source/FlutterWindowController.mm | 78 ++- .../Source/FlutterWindowControllerTest.mm | 25 +- .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../macos/Runner.xcodeproj/project.pbxproj | 589 ++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../macos/Runner/AppDelegate.swift | 20 + .../Runner/Assets.xcassets/Contents.json | 6 + .../macos/Runner/Base.lproj/MainMenu.xib | 332 +++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../multiple_windows/macos/Runner/Info.plist | 32 + .../macos/Runner/Release.entitlements | 8 + .../flutter/lib/src/widgets/_window_io.dart | 3 + .../lib/src/widgets/_window_macos.dart | 566 ++++++++++++++++ 46 files changed, 3142 insertions(+), 87 deletions(-) create mode 100644 dev/devicelab/bin/tasks/windowing_test_macos.dart create mode 100644 dev/integration_tests/windowing_test/macos/.gitignore create mode 100644 dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Podfile create mode 100644 dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj create mode 100644 dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig create mode 100644 dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Info.plist create mode 100644 dev/integration_tests/windowing_test/macos/Runner/Release.entitlements create mode 100644 examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig create mode 100644 examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj create mode 100644 examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/multiple_windows/macos/Runner/AppDelegate.swift create mode 100644 examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json create mode 100644 examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig create mode 100644 examples/multiple_windows/macos/Runner/Configs/Release.xcconfig create mode 100644 examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig create mode 100644 examples/multiple_windows/macos/Runner/DebugProfile.entitlements create mode 100644 examples/multiple_windows/macos/Runner/Info.plist create mode 100644 examples/multiple_windows/macos/Runner/Release.entitlements create mode 100644 packages/flutter/lib/src/widgets/_window_macos.dart diff --git a/.ci.yaml b/.ci.yaml index cd1ba1016f5..2feb5937ebf 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -7005,6 +7005,16 @@ targets: ["devicelab", "hostonly", "linux"] task_name: windowing_test_linux + - name: MacOS windowing_test + recipe: devicelab/devicelab_drone + presubmit: true + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "hostonly", "macos"] + task_name: windowing_test_macos + - name: Windows flutter_tool_startup__windows recipe: devicelab/devicelab_drone diff --git a/TESTOWNERS b/TESTOWNERS index 4e619146f85..1eb191ac0f8 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -306,6 +306,7 @@ /dev/devicelab/bin/tasks/windows_home_scroll_perf__timeline_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/windows_startup_test.dart @loic-sharma @flutter/desktop /dev/devicelab/bin/tasks/windowing_test_linux.dart @robert-ancell @flutter/desktop +/dev/devicelab/bin/tasks/windowing_test_macos.dart @knopp @flutter/desktop /dev/devicelab/bin/tasks/windowing_test_windows.dart @mattkae @flutter/desktop /dev/devicelab/bin/tasks/mac_desktop_impeller.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/linux_desktop_impeller.dart @jonahwilliams @flutter/engine diff --git a/dev/devicelab/bin/tasks/windowing_test_macos.dart b/dev/devicelab/bin/tasks/windowing_test_macos.dart new file mode 100644 index 00000000000..a52c8cf9d3b --- /dev/null +++ b/dev/devicelab/bin/tasks/windowing_test_macos.dart @@ -0,0 +1,12 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/integration_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.macos; + await task(createWindowingDriverTest()); +} diff --git a/dev/integration_tests/windowing_test/lib/main.dart b/dev/integration_tests/windowing_test/lib/main.dart index c40a4564ba5..e20c08d096d 100644 --- a/dev/integration_tests/windowing_test/lib/main.dart +++ b/dev/integration_tests/windowing_test/lib/main.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/_window.dart'; import 'package:flutter_driver/driver_extension.dart'; @@ -56,6 +57,14 @@ void main() { controller.addListener(notificationHandler); act(); + + // On macOS, the predicate is immediately true, even though + // the animation is in progress and next request for state change + // will be ignored. Easiest way to handle this is to just wait. + if (defaultTargetPlatform == TargetPlatform.macOS) { + await Future.delayed(Duration(seconds: 1)); + } + await for (final _ in streamController.stream) { if (predicate()) { break; diff --git a/dev/integration_tests/windowing_test/macos/.gitignore b/dev/integration_tests/windowing_test/macos/.gitignore new file mode 100644 index 00000000000..746adbb6b9e --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000000..4b81f9b2d20 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000000..5caa9d1579e --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Podfile b/dev/integration_tests/windowing_test/macos/Podfile new file mode 100644 index 00000000000..66f6172bbb3 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Podfile @@ -0,0 +1,39 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..f45fd6898b1 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,628 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 18B7BE7286AA73E640D4546D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23A79C077946AA5B54F579AF /* Pods_Runner.framework */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 23A79C077946AA5B54F579AF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 337720BD6C32FB7FE93BBCF6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* windowing_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = windowing_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 47A0D2D6FF1E3F3FAC11013A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 51DCB034E431A572E7D3465E /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 8B4F1AA600E43ABB5D7F8B25 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 8BEBF1BF7D7BB8413407C4F0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A667FFD803107778FA1A5CF1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + EE1DB3CE88B688DF762EB59B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B7BE7286AA73E640D4546D /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + D73B54801185B7255BC68518 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* windowing_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 23A79C077946AA5B54F579AF /* Pods_Runner.framework */, + 51DCB034E431A572E7D3465E /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D73B54801185B7255BC68518 /* Pods */ = { + isa = PBXGroup; + children = ( + EE1DB3CE88B688DF762EB59B /* Pods-Runner.debug.xcconfig */, + 8BEBF1BF7D7BB8413407C4F0 /* Pods-Runner.release.xcconfig */, + A667FFD803107778FA1A5CF1 /* Pods-Runner.profile.xcconfig */, + 47A0D2D6FF1E3F3FAC11013A /* Pods-RunnerTests.debug.xcconfig */, + 337720BD6C32FB7FE93BBCF6 /* Pods-RunnerTests.release.xcconfig */, + 8B4F1AA600E43ABB5D7F8B25 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 490E25A356C91E01B35C3A9E /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* windowing_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 490E25A356C91E01B35C3A9E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..c811dbf0514 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..21a3cc14c74 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift b/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000000..ef951bbb999 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift @@ -0,0 +1,20 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + var engine: FlutterEngine? + + override func applicationDidFinishLaunching(_ notification: Notification) { + engine = FlutterEngine(name: "project", project: nil) + engine?.run(withEntrypoint:nil) + } +} diff --git a/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json b/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib b/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000000..b60f00429f5 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000000..b20d4566925 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = windowing_test + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.windowingTest + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000000..36b0fd9464f --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000000..dff4f49561c --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000000..42bcbf4780b --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements b/dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000000..dddb8a30c85 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/Info.plist b/dev/integration_tests/windowing_test/macos/Runner/Info.plist new file mode 100644 index 00000000000..4789daa6a44 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements b/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements new file mode 100644 index 00000000000..852fa1a4728 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/dev/integration_tests/windowing_test/test_driver/main_test.dart b/dev/integration_tests/windowing_test/test_driver/main_test.dart index 0a7ab93dbc3..5a0d27aab54 100644 --- a/dev/integration_tests/windowing_test/test_driver/main_test.dart +++ b/dev/integration_tests/windowing_test/test_driver/main_test.dart @@ -52,54 +52,6 @@ void main() { expect(data["height"], 600); }, timeout: Timeout.none); - test( - 'Can set constraints and see the resize', - () async { - await driver.requestData( - jsonEncode({ - 'type': 'set_constraints', - 'min_width': 0, - 'min_height': 0, - 'max_width': 500, - 'max_height': 501, - }), - ); - final response = await driver.requestData( - jsonEncode({'type': 'get_size'}), - ); - final data = jsonDecode(response); - expect(data["width"], 500); - expect(data["height"], 501); - }, - timeout: Timeout.none, - onPlatform: {'linux': Skip('Unable to exactly set dimensions on Linux')}, - ); - - test( - 'Can set constraints and see the resize (Linux)', - () async { - await driver.requestData( - jsonEncode({ - 'type': 'set_constraints', - 'min_width': 0, - 'min_height': 0, - 'max_width': 500, - 'max_height': 501, - }), - ); - final response = await driver.requestData( - jsonEncode({'type': 'get_size'}), - ); - final data = jsonDecode(response); - // On Linux setting the constraints limits the window including the decorations, - // but the returned size is the usable area and always smaller. - expect(data["width"], lessThanOrEqualTo(500)); - expect(data["height"], lessThanOrEqualTo(501)); - }, - timeout: Timeout.none, - testOn: "linux", - ); - test('Can set and get fullscreen', () async { await driver.requestData(jsonEncode({'type': 'set_fullscreen'})); var response = await driver.requestData( @@ -180,5 +132,53 @@ void main() { timeout: Timeout.none, onPlatform: {'linux': Skip('Dialogs are not yet supported on Wayland')}, ); + + test( + 'Can set constraints and see the resize', + () async { + await driver.requestData( + jsonEncode({ + 'type': 'set_constraints', + 'min_width': 0, + 'min_height': 0, + 'max_width': 500, + 'max_height': 501, + }), + ); + final response = await driver.requestData( + jsonEncode({'type': 'get_size'}), + ); + final data = jsonDecode(response); + expect(data["width"], 500); + expect(data["height"], 501); + }, + timeout: Timeout.none, + onPlatform: {'linux': Skip('Unable to exactly set dimensions on Linux')}, + ); + + test( + 'Can set constraints and see the resize (Linux)', + () async { + await driver.requestData( + jsonEncode({ + 'type': 'set_constraints', + 'min_width': 0, + 'min_height': 0, + 'max_width': 500, + 'max_height': 501, + }), + ); + final response = await driver.requestData( + jsonEncode({'type': 'get_size'}), + ); + final data = jsonDecode(response); + // On Linux setting the constraints limits the window including the decorations, + // but the returned size is the usable area and always smaller. + expect(data["width"], lessThanOrEqualTo(500)); + expect(data["height"], lessThanOrEqualTo(501)); + }, + timeout: Timeout.none, + testOn: "linux", + ); }); } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h index 39681175cec..ee170d912b4 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -24,11 +24,12 @@ @end -struct FlutterWindowSizing { - bool has_size; +struct FlutterWindowSize { double width; double height; - bool has_constraints; +}; + +struct FlutterWindowConstraints { double min_width; double min_height; double max_width; @@ -36,14 +37,12 @@ struct FlutterWindowSizing { }; struct FlutterWindowCreationRequest { - FlutterWindowSizing contentSize; + bool has_size; + struct FlutterWindowSize size; + bool has_constraints; + struct FlutterWindowConstraints constraints; void (*on_close)(); - void (*on_size_change)(); -}; - -struct FlutterWindowSize { - double width; - double height; + void (*notify_listeners)(); }; extern "C" { @@ -65,7 +64,11 @@ FLUTTER_DARWIN_EXPORT FlutterWindowSize InternalFlutter_Window_GetContentSize(void* window); FLUTTER_DARWIN_EXPORT -void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSizing* size); +void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSize* size); + +FLUTTER_DARWIN_EXPORT +void InternalFlutter_Window_SetConstraints(void* window, + const FlutterWindowConstraints* constraints); FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetTitle(void* window, const char* title); @@ -94,6 +97,12 @@ bool InternalFlutter_Window_IsFullScreen(void* window); FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Activate(void* window); +FLUTTER_DARWIN_EXPORT +char* InternalFlutter_Window_GetTitle(void* window); + +FLUTTER_DARWIN_EXPORT +bool InternalFlutter_Window_IsActivated(void* window); + // NOLINTEND(google-objc-function-naming) } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm index 95aa3548ea5..a4e66babd9b 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -33,22 +33,31 @@ @interface NSWindow (FlutterWindowSizing) -- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize; +- (void)flutterSetContentSize:(FlutterWindowSize)contentSize; +- (void)flutterSetConstraints:(FlutterWindowConstraints)constraints; @end @implementation NSWindow (FlutterWindowSizing) -- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize { - if (contentSize.has_size) { - [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)]; +- (void)flutterSetContentSize:(FlutterWindowSize)contentSize { + [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)]; +} + +- (void)flutterSetConstraints:(FlutterWindowConstraints)constraints { + NSSize size = [self frameRectForContentRect:self.frame].size; + NSSize originalSize = size; + [self setContentMinSize:NSMakeSize(constraints.min_width, constraints.min_height)]; + size.width = std::max(size.width, constraints.min_width); + size.height = std::max(size.height, constraints.min_height); + if (constraints.max_width > 0 && constraints.max_height > 0) { + [self setContentMaxSize:NSMakeSize(constraints.max_width, constraints.max_height)]; + size.width = std::min(size.width, constraints.max_width); + size.height = std::min(size.height, constraints.max_height); + } else { + [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; } - if (contentSize.has_constraints) { - [self setContentMinSize:NSMakeSize(contentSize.min_width, contentSize.min_height)]; - if (contentSize.max_width > 0 && contentSize.max_height > 0) { - [self setContentMaxSize:NSMakeSize(contentSize.max_width, contentSize.max_height)]; - } else { - [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; - } + if (!NSEqualSizes(originalSize, size)) { + [self setContentSize:size]; } } @@ -87,21 +96,31 @@ - (void)windowDidResize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); } // Miniaturize does not trigger resize event, but for now there // is no other way to get notification about the state change. - (void)windowDidMiniaturize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); } // Deminiaturize does not trigger resize event, but for now there // is no other way to get notification about the state change. - (void)windowDidDeminiaturize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); +} + +- (void)windowWillEnterFullScreen:(NSNotification*)notification { + flutter::IsolateScope isolate_scope(*_isolate); + _creationRequest.notify_listeners(); +} + +- (void)windowWillExitFullScreen:(NSNotification*)notification { + flutter::IsolateScope isolate_scope(*_isolate); + _creationRequest.notify_listeners(); } @end @@ -135,7 +154,12 @@ window.contentViewController = c; window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; - [window flutterSetContentSize:request->contentSize]; + if (request->has_size) { + [window flutterSetContentSize:request->size]; + } + if (request->has_constraints) { + [window flutterSetConstraints:request->constraints]; + } [window setIsVisible:YES]; [window makeKeyAndOrderFront:nil]; @@ -200,17 +224,25 @@ void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier FlutterWindowSize InternalFlutter_Window_GetContentSize(void* window) { NSWindow* w = (__bridge NSWindow*)window; + NSRect contentRect = [w contentRectForFrameRect:w.frame]; return { - .width = w.frame.size.width, - .height = w.frame.size.height, + .width = contentRect.size.width, + .height = contentRect.size.height, }; } -void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSizing* size) { +void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSize* size) { NSWindow* w = (__bridge NSWindow*)window; [w flutterSetContentSize:*size]; } +FLUTTER_DARWIN_EXPORT +void InternalFlutter_Window_SetConstraints(void* window, + const FlutterWindowConstraints* constraints) { + NSWindow* w = (__bridge NSWindow*)window; + [w flutterSetConstraints:*constraints]; +} + void InternalFlutter_Window_SetTitle(void* window, const char* title) { NSWindow* w = (__bridge NSWindow*)window; w.title = [NSString stringWithUTF8String:title]; @@ -266,4 +298,14 @@ void InternalFlutter_Window_Activate(void* window) { [w makeKeyAndOrderFront:nil]; } +char* InternalFlutter_Window_GetTitle(void* window) { + NSWindow* w = (__bridge NSWindow*)window; + return strdup(w.title.UTF8String); +} + +bool InternalFlutter_Window_IsActivated(void* window) { + NSWindow* w = (__bridge NSWindow*)window; + return w.isKeyWindow; +} + // NOLINTEND(google-objc-function-naming) diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm index 36068f950f8..ab10904092d 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm @@ -57,9 +57,10 @@ class FlutterWindowControllerRetainTest : public ::testing::Test {}; TEST_F(FlutterWindowControllerTest, CreateRegularWindow) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -80,9 +81,10 @@ TEST_F(FlutterWindowControllerTest, CreateRegularWindow) { TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; __weak FlutterEngine* weakEngine = nil; @@ -121,9 +123,10 @@ TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) { TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -140,9 +143,10 @@ TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) { TEST_F(FlutterWindowControllerTest, InternalFlutter_Window_GetHandle) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -158,9 +162,10 @@ TEST_F(FlutterWindowControllerTest, InternalFlutter_Window_GetHandle) { TEST_F(FlutterWindowControllerTest, WindowStates) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); diff --git a/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig b/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000000..c2efd0b608b --- /dev/null +++ b/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig b/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000000..c2efd0b608b --- /dev/null +++ b/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj b/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..ded159e9d84 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,589 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* multiple_windows.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = multiple_windows.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* multiple_windows.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* multiple_windows.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..a2c5980daec --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata b/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..1d526a16ed0 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multiple_windows/macos/Runner/AppDelegate.swift b/examples/multiple_windows/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000000..ef951bbb999 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/AppDelegate.swift @@ -0,0 +1,20 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + var engine: FlutterEngine? + + override func applicationDidFinishLaunching(_ notification: Notification) { + engine = FlutterEngine(name: "project", project: nil) + engine?.run(withEntrypoint:nil) + } +} diff --git a/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json b/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib b/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000000..d374b6b86f6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig b/examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000000..7a4115b2371 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = multiple_windows + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.multipleWindows + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000000..36b0fd9464f --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000000..dff4f49561c --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000000..42bcbf4780b --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/examples/multiple_windows/macos/Runner/DebugProfile.entitlements b/examples/multiple_windows/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000000..dddb8a30c85 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/examples/multiple_windows/macos/Runner/Info.plist b/examples/multiple_windows/macos/Runner/Info.plist new file mode 100644 index 00000000000..4789daa6a44 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/examples/multiple_windows/macos/Runner/Release.entitlements b/examples/multiple_windows/macos/Runner/Release.entitlements new file mode 100644 index 00000000000..852fa1a4728 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/flutter/lib/src/widgets/_window_io.dart b/packages/flutter/lib/src/widgets/_window_io.dart index 424f88c106e..e86f084bd91 100644 --- a/packages/flutter/lib/src/widgets/_window_io.dart +++ b/packages/flutter/lib/src/widgets/_window_io.dart @@ -20,6 +20,7 @@ import 'package:flutter/foundation.dart'; import '_window.dart'; import '_window_linux.dart'; +import '_window_macos.dart'; import '_window_win32.dart'; /// Creates a default [WindowingOwner] for the current platform. @@ -34,6 +35,8 @@ WindowingOwner? createDefaultOwner() { return WindowingOwnerWin32(); } else if (Platform.isLinux) { return WindowingOwnerLinux(); + } else if (Platform.isMacOS) { + return WindowingOwnerMacOS(); } else { return null; } diff --git a/packages/flutter/lib/src/widgets/_window_macos.dart b/packages/flutter/lib/src/widgets/_window_macos.dart new file mode 100644 index 00000000000..16c4dac015b --- /dev/null +++ b/packages/flutter/lib/src/widgets/_window_macos.dart @@ -0,0 +1,566 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert' show utf8; +import 'dart:ffi' hide Size; +import 'dart:io'; +import 'dart:ui' show Display, FlutterView; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; + +import '../foundation/_features.dart'; +import '_window.dart'; +import 'binding.dart'; + +// Do not import this file in production applications or packages published +// to pub.dev. Flutter will make breaking changes to this file, even in patch +// versions. +// +// All APIs in this file must be private or must: +// +// 1. Have the `@internal` attribute. +// 2. Throw an `UnsupportedError` if `isWindowingEnabled` +// is `false`. +// +// See: https://github.com/flutter/flutter/issues/30701. + +const String _kWindowingDisabledErrorMessage = ''' +Windowing APIs are not enabled. + +Windowing APIs are currently experimental. Do not use windowing APIs in +production applications or plugins published to pub.dev. + +To try experimental windowing APIs: +1. Switch to Flutter's main release channel. +2. Turn on the windowing feature flag. + +See: https://github.com/flutter/flutter/issues/30701. +'''; + +/// [WindowingOwner] implementation for macOS. +/// +/// If [Platform.isMacOS] is false, then the constructor will throw an +/// [UnsupportedError]. +/// +/// {@macro flutter.widgets.windowing.experimental} +/// +/// See also: +/// +/// * [WindowingOwner], the abstract class that manages native windows. +class WindowingOwnerMacOS extends WindowingOwner { + /// Creates a new [WindowingOwnerMacOS] instance. + /// + /// {@macro flutter.widgets.windowing.experimental} + /// + /// See also: + /// + /// * [WindowingOwner], the abstract class that manages native windows. + @internal + WindowingOwnerMacOS() { + if (!isWindowingEnabled) { + throw UnsupportedError(_kWindowingDisabledErrorMessage); + } + + if (!Platform.isMacOS) { + throw UnsupportedError('Only available on the macOS platform'); + } + + assert( + PlatformDispatcher.instance.engineId != null, + 'WindowingOwnerMacOS must be created after the engine has been initialized.', + ); + } + + @override + RegularWindowController createRegularWindowController({ + required RegularWindowControllerDelegate delegate, + Size? preferredSize, + BoxConstraints? preferredConstraints, + String? title, + }) { + final RegularWindowControllerMacOS res = RegularWindowControllerMacOS( + owner: this, + delegate: delegate, + preferredSize: preferredSize, + title: title, + ); + _activeControllers.add(res); + return res; + } + + @override + bool hasTopLevelWindows() { + return _activeControllers.isNotEmpty; + } + + final List _activeControllers = []; + + /// Returns the window handle for the given [view], or null is the window + /// handle is not available. + /// + /// The window handle is a pointer to the NSWindow instance. + static Pointer getWindowHandle(FlutterView view) { + return _MacOSPlatformInterface.getWindowHandle( + PlatformDispatcher.instance.engineId!, + view.viewId, + ); + } + + @override + DialogWindowController createDialogWindowController({ + required DialogWindowControllerDelegate delegate, + Size? preferredSize, + BoxConstraints? preferredConstraints, + BaseWindowController? parent, + String? title, + }) { + throw UnimplementedError(); + } +} + +/// Implementation of [RegularWindowController] for the macOS platform. +/// +/// {@macro flutter.widgets.windowing.experimental} +/// +/// See also: +/// +/// * [RegularWindowController], the base class for regular windows. +class RegularWindowControllerMacOS extends RegularWindowController { + /// Creates a new regular window controller for macOS. When this constructor + /// completes the FlutterView is created and framework is aware of it. + RegularWindowControllerMacOS({ + required WindowingOwnerMacOS owner, + required RegularWindowControllerDelegate delegate, + required Size? preferredSize, + BoxConstraints? preferredConstraints, + String? title, + }) : _owner = owner, + _delegate = delegate, + super.empty() { + if (!isWindowingEnabled) { + throw UnsupportedError(_kWindowingDisabledErrorMessage); + } + + _onClose = NativeCallable.isolateLocal(_handleOnClose); + _onResize = NativeCallable.isolateLocal(_handleOnResize); + final int viewId = _MacOSPlatformInterface.createWindow( + preferredSize: preferredSize, + preferredConstraints: preferredConstraints, + onClose: _onClose.nativeFunction, + onNotifyListeners: _onResize.nativeFunction, + ); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == viewId, + ); + rootView = flutterView; + if (title != null) { + setTitle(title); + } + } + + /// Returns window handle for the current window. + /// + /// The handle is a pointer to an NSWindow instance. + Pointer getWindowHandle() { + _ensureNotDestroyed(); + return WindowingOwnerMacOS.getWindowHandle(rootView); + } + + bool _destroyed = false; + + @override + void destroy() { + if (_destroyed) { + return; + } + final Pointer handle = getWindowHandle(); + _destroyed = true; + _owner._activeControllers.remove(this); + _MacOSPlatformInterface.destroyWindow(handle); + _delegate.onWindowDestroyed(); + _onClose.close(); + _onResize.close(); + } + + void _handleOnClose() { + _delegate.onWindowCloseRequested(this); + } + + void _handleOnResize() { + notifyListeners(); + } + + @override + @internal + void setSize(Size size) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowContentSize(getWindowHandle(), size); + } + + @override + @internal + void setConstraints(BoxConstraints constraints) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowConstraints(getWindowHandle(), constraints); + } + + @override + void setTitle(String title) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowTitle(getWindowHandle(), title); + notifyListeners(); + } + + final WindowingOwnerMacOS _owner; + final RegularWindowControllerDelegate _delegate; + late final NativeCallable _onClose; + late final NativeCallable _onResize; + + @override + Size get contentSize { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.getWindowContentSize(getWindowHandle()); + } + + @override + void activate() { + _ensureNotDestroyed(); + _MacOSPlatformInterface.activate(getWindowHandle()); + } + + @override + void setMaximized(bool maximized) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setMaximized(getWindowHandle(), maximized); + } + + @override + bool get isMaximized { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isMaximized(getWindowHandle()); + } + + @override + void setMinimized(bool minimized) { + _ensureNotDestroyed(); + if (minimized) { + _MacOSPlatformInterface.minimize(getWindowHandle()); + } else { + _MacOSPlatformInterface.unminimize(getWindowHandle()); + } + } + + @override + bool get isMinimized { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isMinimized(getWindowHandle()); + } + + @override + void setFullscreen(bool fullscreen, {Display? display}) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setFullscreen(getWindowHandle(), fullscreen); + } + + @override + bool get isFullscreen { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isFullscreen(getWindowHandle()); + } + + void _ensureNotDestroyed() { + if (_destroyed) { + throw StateError('Window has been destroyed.'); + } + } + + @override + bool get isActivated => _MacOSPlatformInterface.isActivated(getWindowHandle()); + + @override + String get title => _MacOSPlatformInterface.getTitle(getWindowHandle()); +} + +final class _WindowCreationRequest extends Struct { + @Bool() + external bool hasSize; + external _Size contentSize; + + @Bool() + external bool hasConstraints; + external _Constraints constraints; + + external Pointer> onClose; + external Pointer> onNotifyListeners; +} + +final class _Size extends Struct { + @Double() + external double width; + + @Double() + external double height; +} + +final class _Constraints extends Struct { + @Double() + external double minWidth; + + @Double() + external double minHeight; + + @Double() + external double maxWidth; + + @Double() + external double maxHeight; +} + +class _MacOSPlatformInterface { + @Native Function(Int64, Int64)>(symbol: 'InternalFlutter_Window_GetHandle') + external static Pointer getWindowHandle(int engineId, int viewId); + + @Native, Pointer<_Size>)>( + symbol: 'InternalFlutter_Window_SetContentSize', + ) + external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Size> size); + + static void setWindowContentSize(Pointer windowHandle, Size size) { + final Pointer<_Size> ffiSize = _allocator<_Size>(); + ffiSize.ref + ..width = size.width + ..height = size.height; + _setWindowContentSize(windowHandle, ffiSize); + _allocator.free(ffiSize); + } + + @Native, Pointer<_Constraints>)>( + symbol: 'InternalFlutter_Window_SetConstraints', + ) + external static void _setWindowConstraints( + Pointer windowHandle, + Pointer<_Constraints> size, + ); + + static void setWindowConstraints(Pointer windowHandle, BoxConstraints constraints) { + final Pointer<_Constraints> ffiConstraints = _allocator<_Constraints>(); + ffiConstraints.ref + ..minWidth = constraints.minWidth + ..minHeight = constraints.minHeight + ..maxWidth = constraints.maxWidth + ..maxHeight = constraints.maxHeight; + + _setWindowConstraints(windowHandle, ffiConstraints); + _allocator.free(ffiConstraints); + } + + @Native)>( + symbol: 'InternalFlutter_WindowController_CreateRegularWindow', + ) + external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); + + /// Creates a new window and returns the viewId of the created FlutterView. + static int createWindow({ + required Size? preferredSize, + BoxConstraints? preferredConstraints, + required Pointer> onClose, + required Pointer> onNotifyListeners, + }) { + final Pointer<_WindowCreationRequest> request = _allocator<_WindowCreationRequest>() + ..ref.onClose = onClose + ..ref.onNotifyListeners = onNotifyListeners; + + if (preferredSize != null) { + request.ref + ..hasSize = true + ..contentSize.width = preferredSize.width + ..contentSize.height = preferredSize.height; + } + + if (preferredConstraints != null) { + request.ref + ..hasConstraints = true + ..constraints.minWidth = preferredConstraints.minWidth + ..constraints.minHeight = preferredConstraints.minHeight + ..constraints.maxWidth = preferredConstraints.maxWidth + ..constraints.maxHeight = preferredConstraints.maxHeight; + } + final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); + _allocator.free(request); + return viewId; + } + + @Native)>(symbol: 'InternalFlutter_Window_Destroy') + external static void _destroyWindow(int engineId, Pointer handle); + + static void destroyWindow(Pointer windowHandle) { + _destroyWindow(PlatformDispatcher.instance.engineId!, windowHandle); + } + + @Native<_Size Function(Pointer)>(symbol: 'InternalFlutter_Window_GetContentSize') + external static _Size _getWindowContentSize(Pointer windowHandle); + + static Size getWindowContentSize(Pointer windowHandle) { + final _Size size = _getWindowContentSize(windowHandle); + return Size(size.width, size.height); + } + + @Native, Pointer<_Utf8>)>(symbol: 'InternalFlutter_Window_SetTitle') + external static void _setWindowTitle(Pointer windowHandle, Pointer<_Utf8> title); + + static void setWindowTitle(Pointer windowHandle, String title) { + final Pointer<_Utf8> titlePointer = title.toNativeUtf8(); + _setWindowTitle(windowHandle, titlePointer); + _allocator.free(titlePointer); + } + + @Native, Bool)>(symbol: 'InternalFlutter_Window_SetMaximized') + external static void setMaximized(Pointer windowHandle, bool maximized); + + @Native)>(symbol: 'InternalFlutter_Window_IsMaximized') + external static bool isMaximized(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Minimize') + external static void minimize(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Unminimize') + external static void unminimize(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_IsMinimized') + external static bool isMinimized(Pointer windowHandle); + + @Native, Bool)>(symbol: 'InternalFlutter_Window_SetFullScreen') + external static void setFullscreen(Pointer windowHandle, bool fullscreen); + + @Native)>(symbol: 'InternalFlutter_Window_IsFullScreen') + external static bool isFullscreen(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Activate') + external static void activate(Pointer windowHandle); + + @Native Function(Pointer)>(symbol: 'InternalFlutter_Window_GetTitle') + external static Pointer<_Utf8> _getTitle(Pointer windowHandle); + + static String getTitle(Pointer windowHandle) { + final Pointer<_Utf8> title = _getTitle(windowHandle); + final String result = title.toDartString(); + _allocator.free(title); + return result; + } + + @Native)>(symbol: 'InternalFlutter_Window_IsActivated') + external static bool isActivated(Pointer windowHandle); +} + +// FFI utilities. + +typedef _PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); + +@Native<_PosixCallocNative>(symbol: 'calloc') +external Pointer _posixCalloc(int num, int size); + +typedef _PosixFreeNative = Void Function(Pointer); + +@Native)>(symbol: 'free') +external void _posixFree(Pointer ptr); + +final Pointer> _posixFreePointer = + Native.addressOf>(_posixFree); + +const _CallocAllocator _allocator = _CallocAllocator._(); + +final class _CallocAllocator implements Allocator { + const _CallocAllocator._(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + @override + Pointer allocate(int byteCount, {int? alignment}) { + final Pointer result = _posixCalloc(byteCount, 1).cast(); + + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + @override + void free(Pointer pointer) { + _posixFree(pointer); + } + + /// Returns a pointer to a native free function. + Pointer get nativeFree => _posixFreePointer; +} + +/// The contents of a native zero-terminated array of UTF-8 code units. +/// +/// The Utf8 type itself has no functionality, it's only intended to be used +/// through a `Pointer` representing the entire array. This pointer is +/// the equivalent of a char pointer (`const char*`) in C code. +final class _Utf8 extends Opaque {} + +/// Extension method for converting a`Pointer` to a [String]. +extension _Utf8Pointer on Pointer<_Utf8> { + /// Converts this UTF-8 encoded string to a Dart string. + /// + /// Decodes the UTF-8 code units of this zero-terminated byte array as + /// Unicode code points and creates a Dart string containing those code + /// points. + /// + /// If [length] is provided, zero-termination is ignored and the result can + /// contain NUL characters. + /// + /// If [length] is not provided, the returned string is the string up til + /// but not including the first NUL character. + String toDartString({int? length}) { + _ensureNotNullptr('toDartString'); + final Pointer codeUnits = cast(); + if (length != null) { + RangeError.checkNotNegative(length, 'length'); + } else { + length = _length(codeUnits); + } + return utf8.decode(codeUnits.asTypedList(length)); + } + + static int _length(Pointer codeUnits) { + int length = 0; + while (codeUnits[length] != 0) { + length++; + } + return length; + } + + void _ensureNotNullptr(String operation) { + if (this == nullptr) { + throw UnsupportedError("Operation '$operation' not allowed on a 'nullptr'."); + } + } +} + +/// Extension method for converting a [String] to a `Pointer`. +extension _StringUtf8Pointer on String { + /// Creates a zero-terminated [_Utf8] code-unit array from this String. + /// + /// If this [String] contains NUL characters, converting it back to a string + /// using [_Utf8Pointer.toDartString] will truncate the result if a length is + /// not passed. + /// + /// Unpaired surrogate code points in this [String] will be encoded as + /// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in + /// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding. + /// + /// Returns an [allocator]-allocated pointer to the result. + Pointer<_Utf8> toNativeUtf8({Allocator allocator = _allocator}) { + final Uint8List units = utf8.encode(this); + final Pointer result = allocator(units.length + 1); + final Uint8List nativeString = result.asTypedList(units.length + 1); + nativeString.setAll(0, units); + nativeString[units.length] = 0; + return result.cast(); + } +}