Showing
34 changed files
with
1270 additions
and
152 deletions
@@ -5,155 +5,3 @@ | @@ -5,155 +5,3 @@ | ||
5 | 5 | ||
6 |  | 6 |  |
7 |  | 7 |  |
8 | - | ||
9 | -```dart | ||
10 | -void main() { | ||
11 | - WidgetsFlutterBinding.ensureInitialized(); | ||
12 | - //Set the fit size (fill in the screen size of the device in the design) If the design is based on the size of the iPhone6 (iPhone6 750*1334) | ||
13 | - ScreenUtil.init(designSize: Size(750, 1334)); | ||
14 | - runApp(MyApp()); | ||
15 | -} | ||
16 | - | ||
17 | -class MyApp extends StatelessWidget { | ||
18 | - @override | ||
19 | - Widget build(BuildContext context) { | ||
20 | - return MaterialApp( | ||
21 | - debugShowCheckedModeBanner: false, | ||
22 | - title: 'Flutter_ScreenUtil', | ||
23 | - theme: ThemeData( | ||
24 | - primarySwatch: Colors.blue, | ||
25 | - ), | ||
26 | - home: ExampleWidget(title: 'FlutterScreenUtil Demo'), | ||
27 | - ); | ||
28 | - } | ||
29 | -} | ||
30 | - | ||
31 | -class ExampleWidget extends StatefulWidget { | ||
32 | - const ExampleWidget({Key key, this.title}) : super(key: key); | ||
33 | - | ||
34 | - final String title; | ||
35 | - | ||
36 | - @override | ||
37 | - _ExampleWidgetState createState() => _ExampleWidgetState(); | ||
38 | -} | ||
39 | - | ||
40 | -class _ExampleWidgetState extends State<ExampleWidget> { | ||
41 | - @override | ||
42 | - Widget build(BuildContext context) { | ||
43 | - printScreenInformation(); | ||
44 | - return Scaffold( | ||
45 | - appBar: AppBar( | ||
46 | - title: Text(widget.title), | ||
47 | - ), | ||
48 | - body: SingleChildScrollView( | ||
49 | - child: Column( | ||
50 | - crossAxisAlignment: CrossAxisAlignment.center, | ||
51 | - children: <Widget>[ | ||
52 | - Row( | ||
53 | - children: <Widget>[ | ||
54 | - // Using Extensions | ||
55 | - Container( | ||
56 | - padding: EdgeInsets.all(10.w), | ||
57 | - width: 0.5.wp, | ||
58 | - height: 200.h, | ||
59 | - color: Colors.red, | ||
60 | - child: Text( | ||
61 | - 'My width:${0.5.wp}dp \n' | ||
62 | - 'My height:${200.h}dp', | ||
63 | - style: TextStyle( | ||
64 | - color: Colors.white, | ||
65 | - fontSize: 24.sp, | ||
66 | - ), | ||
67 | - ), | ||
68 | - ), | ||
69 | - // Without using Extensions | ||
70 | - Container( | ||
71 | - padding: EdgeInsets.all(ScreenUtil().setWidth(10)), | ||
72 | - width: ScreenUtil().screenWidth * 0.5, | ||
73 | - height: ScreenUtil().setHeight(200), | ||
74 | - color: Colors.blue, | ||
75 | - child: Text( | ||
76 | - 'My width:${ScreenUtil().screenWidth * 0.5}dp \n' | ||
77 | - 'My height:${ScreenUtil().setHeight(200)}dp', | ||
78 | - style: TextStyle( | ||
79 | - color: Colors.white, | ||
80 | - fontSize: ScreenUtil().setSp(24), | ||
81 | - ), | ||
82 | - ), | ||
83 | - ), | ||
84 | - ], | ||
85 | - ), | ||
86 | - Text('Device width:${ScreenUtil().screenWidthPx}px'), | ||
87 | - Text('Device height:${ScreenUtil().screenHeightPx}px'), | ||
88 | - Text('Device width:${ScreenUtil().screenWidth}dp'), | ||
89 | - Text('Device height:${ScreenUtil().screenHeight}dp'), | ||
90 | - Text('Device pixel density:${ScreenUtil().pixelRatio}'), | ||
91 | - Text('Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'), | ||
92 | - Text('Status bar height:${ScreenUtil().statusBarHeight}dp'), | ||
93 | - Text( | ||
94 | - 'Ratio of actual width dp to design draft px:${ScreenUtil().scaleWidth}', | ||
95 | - textAlign: TextAlign.center, | ||
96 | - ), | ||
97 | - Text( | ||
98 | - 'Ratio of actual height dp to design draft px:${ScreenUtil().scaleHeight}', | ||
99 | - textAlign: TextAlign.center, | ||
100 | - ), | ||
101 | - SizedBox( | ||
102 | - height: ScreenUtil().setHeight(100), | ||
103 | - ), | ||
104 | - Text('System font scaling factor:${ScreenUtil().textScaleFactor}'), | ||
105 | - Column( | ||
106 | - crossAxisAlignment: CrossAxisAlignment.start, | ||
107 | - children: <Widget>[ | ||
108 | - Text( | ||
109 | - 'My font size is 24px on the design draft and will not change with the system.', | ||
110 | - style: TextStyle( | ||
111 | - color: Colors.black, | ||
112 | - fontSize: 24.sp, | ||
113 | - ), | ||
114 | - ), | ||
115 | - Text( | ||
116 | - 'My font size is 24px on the design draft and will change with the system.', | ||
117 | - style: ts.t1, | ||
118 | - ), | ||
119 | - ], | ||
120 | - ) | ||
121 | - ], | ||
122 | - ), | ||
123 | - ), | ||
124 | - floatingActionButton: FloatingActionButton( | ||
125 | - child: Icon(Icons.title), | ||
126 | - onPressed: () { | ||
127 | - ScreenUtil.init( | ||
128 | - designSize: Size(1500, 1334), | ||
129 | - ); | ||
130 | - setState(() {}); | ||
131 | - }, | ||
132 | - ), | ||
133 | - ); | ||
134 | - } | ||
135 | - | ||
136 | - void printScreenInformation() { | ||
137 | - print('Device width dp:${ScreenUtil().screenWidth}'); //Device width | ||
138 | - print('Device height dp:${ScreenUtil().screenHeight}'); //Device height | ||
139 | - print( | ||
140 | - 'Device pixel density:${ScreenUtil().pixelRatio}'); //Device pixel density | ||
141 | - print( | ||
142 | - 'Bottom safe zone distance dp:${ScreenUtil().bottomBarHeight}'); //Bottom safe zone distance,suitable for buttons with full screen | ||
143 | - print( | ||
144 | - 'Status bar height px:${ScreenUtil().statusBarHeight}dp'); //Status bar height , Notch will be higher Unit px | ||
145 | - print( | ||
146 | - 'Ratio of actual width dp to design draft px:${ScreenUtil().scaleWidth}'); | ||
147 | - print( | ||
148 | - 'Ratio of actual height dp to design draft px:${ScreenUtil().scaleHeight}'); | ||
149 | - print( | ||
150 | - 'The ratio of font and width to the size of the design:${ScreenUtil().scaleWidth * ScreenUtil().pixelRatio}'); | ||
151 | - print( | ||
152 | - 'The ratio of height width to the size of the design:${ScreenUtil().scaleHeight * ScreenUtil().pixelRatio}'); | ||
153 | - print('System font scaling:${ScreenUtil().textScaleFactor}'); | ||
154 | - print('0.5 times the screen width:${0.5.wp}'); | ||
155 | - print('0.5 times the screen height:${0.5.hp}'); | ||
156 | - } | ||
157 | -} | ||
158 | -``` | ||
159 | - |
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<!-- Modify this file to customize your launch splash screen --> | ||
3 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||
4 | + <item android:drawable="?android:colorBackground" /> | ||
5 | + | ||
6 | + <!-- You can insert your own image assets here --> | ||
7 | + <!-- <item> | ||
8 | + <bitmap | ||
9 | + android:gravity="center" | ||
10 | + android:src="@mipmap/launch_image" /> | ||
11 | + </item> --> | ||
12 | +</layer-list> |
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<resources> | ||
3 | + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | ||
4 | + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
5 | + <!-- Show a splash screen on the activity. Automatically removed when | ||
6 | + Flutter draws its first frame --> | ||
7 | + <item name="android:windowBackground">@drawable/launch_background</item> | ||
8 | + </style> | ||
9 | + <!-- Theme applied to the Android Window as soon as the process has started. | ||
10 | + This theme determines the color of the Android Window while your | ||
11 | + Flutter UI initializes, as well as behind your Flutter UI while its | ||
12 | + running. | ||
13 | + | ||
14 | + This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||
15 | + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
16 | + <item name="android:windowBackground">?android:colorBackground</item> | ||
17 | + </style> | ||
18 | +</resources> |
example/ios/Runner/AppDelegate.swift
0 → 100644
1 | +import UIKit | ||
2 | +import Flutter | ||
3 | + | ||
4 | +@UIApplicationMain | ||
5 | +@objc class AppDelegate: FlutterAppDelegate { | ||
6 | + override func application( | ||
7 | + _ application: UIApplication, | ||
8 | + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | ||
9 | + ) -> Bool { | ||
10 | + GeneratedPluginRegistrant.register(with: self) | ||
11 | + return super.application(application, didFinishLaunchingWithOptions: launchOptions) | ||
12 | + } | ||
13 | +} |
example/ios/Runner/Runner-Bridging-Header.h
0 → 100644
1 | +#import "GeneratedPluginRegistrant.h" |
example/test/widget_test.dart
0 → 100644
1 | +// This is a basic Flutter widget test. | ||
2 | +// | ||
3 | +// To perform an interaction with a widget in your test, use the WidgetTester | ||
4 | +// utility that Flutter provides. For example, you can send tap and scroll | ||
5 | +// gestures. You can also use WidgetTester to find child widgets in the widget | ||
6 | +// tree, read text, and verify that the values of widget properties are correct. | ||
7 | + | ||
8 | +import 'package:flutter/material.dart'; | ||
9 | +import 'package:flutter_test/flutter_test.dart'; | ||
10 | + | ||
11 | +import 'package:example/main.dart'; | ||
12 | + | ||
13 | +void main() { | ||
14 | + testWidgets('Counter increments smoke test', (WidgetTester tester) async { | ||
15 | + // Build our app and trigger a frame. | ||
16 | + await tester.pumpWidget(MyApp()); | ||
17 | + | ||
18 | + // Verify that our counter starts at 0. | ||
19 | + expect(find.text('0'), findsOneWidget); | ||
20 | + expect(find.text('1'), findsNothing); | ||
21 | + | ||
22 | + // Tap the '+' icon and trigger a frame. | ||
23 | + await tester.tap(find.byIcon(Icons.add)); | ||
24 | + await tester.pump(); | ||
25 | + | ||
26 | + // Verify that our counter has incremented. | ||
27 | + expect(find.text('0'), findsNothing); | ||
28 | + expect(find.text('1'), findsOneWidget); | ||
29 | + }); | ||
30 | +} |
example/web/favicon.png
0 → 100644

917 Bytes
example/web/icons/Icon-192.png
0 → 100644

5.17 KB
example/web/icons/Icon-512.png
0 → 100644

8.06 KB
example/web/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | +<head> | ||
4 | + <!-- | ||
5 | + If you are serving your web app in a path other than the root, change the | ||
6 | + href value below to reflect the base path you are serving from. | ||
7 | + | ||
8 | + The path provided below has to start and end with a slash "/" in order for | ||
9 | + it to work correctly. | ||
10 | + | ||
11 | + Fore more details: | ||
12 | + * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base | ||
13 | + --> | ||
14 | + <base href="/"> | ||
15 | + | ||
16 | + <meta charset="UTF-8"> | ||
17 | + <meta content="IE=Edge" http-equiv="X-UA-Compatible"> | ||
18 | + <meta name="description" content="A new Flutter project."> | ||
19 | + | ||
20 | + <!-- iOS meta tags & icons --> | ||
21 | + <meta name="apple-mobile-web-app-capable" content="yes"> | ||
22 | + <meta name="apple-mobile-web-app-status-bar-style" content="black"> | ||
23 | + <meta name="apple-mobile-web-app-title" content="example"> | ||
24 | + <link rel="apple-touch-icon" href="icons/Icon-192.png"> | ||
25 | + | ||
26 | + <!-- Favicon --> | ||
27 | + <link rel="icon" type="image/png" href="favicon.png"/> | ||
28 | + | ||
29 | + <title>example</title> | ||
30 | + <link rel="manifest" href="manifest.json"> | ||
31 | +</head> | ||
32 | +<body> | ||
33 | + <!-- This script installs service_worker.js to provide PWA functionality to | ||
34 | + application. For more information, see: | ||
35 | + https://developers.google.com/web/fundamentals/primers/service-workers --> | ||
36 | + <script> | ||
37 | + if ('serviceWorker' in navigator) { | ||
38 | + window.addEventListener('flutter-first-frame', function () { | ||
39 | + navigator.serviceWorker.register('flutter_service_worker.js'); | ||
40 | + }); | ||
41 | + } | ||
42 | + </script> | ||
43 | + <script src="main.dart.js" type="application/javascript"></script> | ||
44 | +</body> | ||
45 | +</html> |
example/web/manifest.json
0 → 100644
1 | +{ | ||
2 | + "name": "example", | ||
3 | + "short_name": "example", | ||
4 | + "start_url": ".", | ||
5 | + "display": "standalone", | ||
6 | + "background_color": "#0175C2", | ||
7 | + "theme_color": "#0175C2", | ||
8 | + "description": "A new Flutter project.", | ||
9 | + "orientation": "portrait-primary", | ||
10 | + "prefer_related_applications": false, | ||
11 | + "icons": [ | ||
12 | + { | ||
13 | + "src": "icons/Icon-192.png", | ||
14 | + "sizes": "192x192", | ||
15 | + "type": "image/png" | ||
16 | + }, | ||
17 | + { | ||
18 | + "src": "icons/Icon-512.png", | ||
19 | + "sizes": "512x512", | ||
20 | + "type": "image/png" | ||
21 | + } | ||
22 | + ] | ||
23 | +} |
example/windows/.gitignore
0 → 100644
1 | +flutter/ephemeral/ | ||
2 | + | ||
3 | +# Visual Studio user-specific files. | ||
4 | +*.suo | ||
5 | +*.user | ||
6 | +*.userosscache | ||
7 | +*.sln.docstates | ||
8 | + | ||
9 | +# Visual Studio build-related files. | ||
10 | +x64/ | ||
11 | +x86/ | ||
12 | + | ||
13 | +# Visual Studio cache files | ||
14 | +# files ending in .cache can be ignored | ||
15 | +*.[Cc]ache | ||
16 | +# but keep track of directories ending in .cache | ||
17 | +!*.[Cc]ache/ |
example/windows/CMakeLists.txt
0 → 100644
1 | +cmake_minimum_required(VERSION 3.15) | ||
2 | +project(example LANGUAGES CXX) | ||
3 | + | ||
4 | +set(BINARY_NAME "example") | ||
5 | + | ||
6 | +cmake_policy(SET CMP0063 NEW) | ||
7 | + | ||
8 | +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") | ||
9 | + | ||
10 | +# Configure build options. | ||
11 | +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | ||
12 | +if(IS_MULTICONFIG) | ||
13 | + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" | ||
14 | + CACHE STRING "" FORCE) | ||
15 | +else() | ||
16 | + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) | ||
17 | + set(CMAKE_BUILD_TYPE "Debug" CACHE | ||
18 | + STRING "Flutter build mode" FORCE) | ||
19 | + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS | ||
20 | + "Debug" "Profile" "Release") | ||
21 | + endif() | ||
22 | +endif() | ||
23 | + | ||
24 | +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") | ||
25 | +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") | ||
26 | +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") | ||
27 | +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") | ||
28 | + | ||
29 | +# Use Unicode for all projects. | ||
30 | +add_definitions(-DUNICODE -D_UNICODE) | ||
31 | + | ||
32 | +# Compilation settings that should be applied to most targets. | ||
33 | +function(APPLY_STANDARD_SETTINGS TARGET) | ||
34 | + target_compile_features(${TARGET} PUBLIC cxx_std_17) | ||
35 | + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") | ||
36 | + target_compile_options(${TARGET} PRIVATE /EHsc) | ||
37 | + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") | ||
38 | + target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>") | ||
39 | +endfunction() | ||
40 | + | ||
41 | +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") | ||
42 | + | ||
43 | +# Flutter library and tool build rules. | ||
44 | +add_subdirectory(${FLUTTER_MANAGED_DIR}) | ||
45 | + | ||
46 | +# Application build | ||
47 | +add_subdirectory("runner") | ||
48 | + | ||
49 | +# Generated plugin build rules, which manage building the plugins and adding | ||
50 | +# them to the application. | ||
51 | +include(flutter/generated_plugins.cmake) | ||
52 | + | ||
53 | + | ||
54 | +# === Installation === | ||
55 | +# Support files are copied into place next to the executable, so that it can | ||
56 | +# run in place. This is done instead of making a separate bundle (as on Linux) | ||
57 | +# so that building and running from within Visual Studio will work. | ||
58 | +set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>") | ||
59 | +# Make the "install" step default, as it's required to run. | ||
60 | +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) | ||
61 | +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) | ||
62 | + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) | ||
63 | +endif() | ||
64 | + | ||
65 | +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") | ||
66 | +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") | ||
67 | + | ||
68 | +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" | ||
69 | + COMPONENT Runtime) | ||
70 | + | ||
71 | +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" | ||
72 | + COMPONENT Runtime) | ||
73 | + | ||
74 | +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" | ||
75 | + COMPONENT Runtime) | ||
76 | + | ||
77 | +if(PLUGIN_BUNDLED_LIBRARIES) | ||
78 | + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" | ||
79 | + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" | ||
80 | + COMPONENT Runtime) | ||
81 | +endif() | ||
82 | + | ||
83 | +# Fully re-copy the assets directory on each build to avoid having stale files | ||
84 | +# from a previous install. | ||
85 | +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") | ||
86 | +install(CODE " | ||
87 | + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") | ||
88 | + " COMPONENT Runtime) | ||
89 | +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" | ||
90 | + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) | ||
91 | + | ||
92 | +# Install the AOT library on non-Debug builds only. | ||
93 | +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" | ||
94 | + CONFIGURATIONS Profile;Release | ||
95 | + COMPONENT Runtime) |
example/windows/flutter/CMakeLists.txt
0 → 100644
1 | +cmake_minimum_required(VERSION 3.15) | ||
2 | + | ||
3 | +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") | ||
4 | + | ||
5 | +# Configuration provided via flutter tool. | ||
6 | +include(${EPHEMERAL_DIR}/generated_config.cmake) | ||
7 | + | ||
8 | +# TODO: Move the rest of this into files in ephemeral. See | ||
9 | +# https://github.com/flutter/flutter/issues/57146. | ||
10 | +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") | ||
11 | + | ||
12 | +# === Flutter Library === | ||
13 | +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") | ||
14 | + | ||
15 | +# Published to parent scope for install step. | ||
16 | +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) | ||
17 | +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) | ||
18 | +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) | ||
19 | +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) | ||
20 | + | ||
21 | +list(APPEND FLUTTER_LIBRARY_HEADERS | ||
22 | + "flutter_export.h" | ||
23 | + "flutter_windows.h" | ||
24 | + "flutter_messenger.h" | ||
25 | + "flutter_plugin_registrar.h" | ||
26 | + "flutter_texture_registrar.h" | ||
27 | +) | ||
28 | +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") | ||
29 | +add_library(flutter INTERFACE) | ||
30 | +target_include_directories(flutter INTERFACE | ||
31 | + "${EPHEMERAL_DIR}" | ||
32 | +) | ||
33 | +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") | ||
34 | +add_dependencies(flutter flutter_assemble) | ||
35 | + | ||
36 | +# === Wrapper === | ||
37 | +list(APPEND CPP_WRAPPER_SOURCES_CORE | ||
38 | + "core_implementations.cc" | ||
39 | + "standard_codec.cc" | ||
40 | +) | ||
41 | +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") | ||
42 | +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN | ||
43 | + "plugin_registrar.cc" | ||
44 | +) | ||
45 | +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") | ||
46 | +list(APPEND CPP_WRAPPER_SOURCES_APP | ||
47 | + "flutter_engine.cc" | ||
48 | + "flutter_view_controller.cc" | ||
49 | +) | ||
50 | +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") | ||
51 | + | ||
52 | +# Wrapper sources needed for a plugin. | ||
53 | +add_library(flutter_wrapper_plugin STATIC | ||
54 | + ${CPP_WRAPPER_SOURCES_CORE} | ||
55 | + ${CPP_WRAPPER_SOURCES_PLUGIN} | ||
56 | +) | ||
57 | +apply_standard_settings(flutter_wrapper_plugin) | ||
58 | +set_target_properties(flutter_wrapper_plugin PROPERTIES | ||
59 | + POSITION_INDEPENDENT_CODE ON) | ||
60 | +set_target_properties(flutter_wrapper_plugin PROPERTIES | ||
61 | + CXX_VISIBILITY_PRESET hidden) | ||
62 | +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) | ||
63 | +target_include_directories(flutter_wrapper_plugin PUBLIC | ||
64 | + "${WRAPPER_ROOT}/include" | ||
65 | +) | ||
66 | +add_dependencies(flutter_wrapper_plugin flutter_assemble) | ||
67 | + | ||
68 | +# Wrapper sources needed for the runner. | ||
69 | +add_library(flutter_wrapper_app STATIC | ||
70 | + ${CPP_WRAPPER_SOURCES_CORE} | ||
71 | + ${CPP_WRAPPER_SOURCES_APP} | ||
72 | +) | ||
73 | +apply_standard_settings(flutter_wrapper_app) | ||
74 | +target_link_libraries(flutter_wrapper_app PUBLIC flutter) | ||
75 | +target_include_directories(flutter_wrapper_app PUBLIC | ||
76 | + "${WRAPPER_ROOT}/include" | ||
77 | +) | ||
78 | +add_dependencies(flutter_wrapper_app flutter_assemble) | ||
79 | + | ||
80 | +# === Flutter tool backend === | ||
81 | +# _phony_ is a non-existent file to force this command to run every time, | ||
82 | +# since currently there's no way to get a full input/output list from the | ||
83 | +# flutter tool. | ||
84 | +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") | ||
85 | +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) | ||
86 | +add_custom_command( | ||
87 | + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} | ||
88 | + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} | ||
89 | + ${CPP_WRAPPER_SOURCES_APP} | ||
90 | + ${PHONY_OUTPUT} | ||
91 | + COMMAND ${CMAKE_COMMAND} -E env | ||
92 | + ${FLUTTER_TOOL_ENVIRONMENT} | ||
93 | + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" | ||
94 | + windows-x64 $<CONFIG> | ||
95 | + VERBATIM | ||
96 | +) | ||
97 | +add_custom_target(flutter_assemble DEPENDS | ||
98 | + "${FLUTTER_LIBRARY}" | ||
99 | + ${FLUTTER_LIBRARY_HEADERS} | ||
100 | + ${CPP_WRAPPER_SOURCES_CORE} | ||
101 | + ${CPP_WRAPPER_SOURCES_PLUGIN} | ||
102 | + ${CPP_WRAPPER_SOURCES_APP} | ||
103 | +) |
1 | +// | ||
2 | +// Generated file. Do not edit. | ||
3 | +// | ||
4 | + | ||
5 | +#ifndef GENERATED_PLUGIN_REGISTRANT_ | ||
6 | +#define GENERATED_PLUGIN_REGISTRANT_ | ||
7 | + | ||
8 | +#include <flutter/plugin_registry.h> | ||
9 | + | ||
10 | +// Registers Flutter plugins. | ||
11 | +void RegisterPlugins(flutter::PluginRegistry* registry); | ||
12 | + | ||
13 | +#endif // GENERATED_PLUGIN_REGISTRANT_ |
1 | +# | ||
2 | +# Generated file, do not edit. | ||
3 | +# | ||
4 | + | ||
5 | +list(APPEND FLUTTER_PLUGIN_LIST | ||
6 | +) | ||
7 | + | ||
8 | +set(PLUGIN_BUNDLED_LIBRARIES) | ||
9 | + | ||
10 | +foreach(plugin ${FLUTTER_PLUGIN_LIST}) | ||
11 | + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) | ||
12 | + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) | ||
13 | + list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>) | ||
14 | + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) | ||
15 | +endforeach(plugin) |
example/windows/runner/CMakeLists.txt
0 → 100644
1 | +cmake_minimum_required(VERSION 3.15) | ||
2 | +project(runner LANGUAGES CXX) | ||
3 | + | ||
4 | +add_executable(${BINARY_NAME} WIN32 | ||
5 | + "flutter_window.cpp" | ||
6 | + "main.cpp" | ||
7 | + "run_loop.cpp" | ||
8 | + "utils.cpp" | ||
9 | + "win32_window.cpp" | ||
10 | + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" | ||
11 | + "Runner.rc" | ||
12 | + "runner.exe.manifest" | ||
13 | +) | ||
14 | +apply_standard_settings(${BINARY_NAME}) | ||
15 | +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") | ||
16 | +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) | ||
17 | +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") | ||
18 | +add_dependencies(${BINARY_NAME} flutter_assemble) |
example/windows/runner/Runner.rc
0 → 100644
1 | +// Microsoft Visual C++ generated resource script. | ||
2 | +// | ||
3 | +#pragma code_page(65001) | ||
4 | +#include "resource.h" | ||
5 | + | ||
6 | +#define APSTUDIO_READONLY_SYMBOLS | ||
7 | +///////////////////////////////////////////////////////////////////////////// | ||
8 | +// | ||
9 | +// Generated from the TEXTINCLUDE 2 resource. | ||
10 | +// | ||
11 | +#include "winres.h" | ||
12 | + | ||
13 | +///////////////////////////////////////////////////////////////////////////// | ||
14 | +#undef APSTUDIO_READONLY_SYMBOLS | ||
15 | + | ||
16 | +///////////////////////////////////////////////////////////////////////////// | ||
17 | +// English (United States) resources | ||
18 | + | ||
19 | +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) | ||
20 | +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US | ||
21 | + | ||
22 | +#ifdef APSTUDIO_INVOKED | ||
23 | +///////////////////////////////////////////////////////////////////////////// | ||
24 | +// | ||
25 | +// TEXTINCLUDE | ||
26 | +// | ||
27 | + | ||
28 | +1 TEXTINCLUDE | ||
29 | +BEGIN | ||
30 | + "resource.h\0" | ||
31 | +END | ||
32 | + | ||
33 | +2 TEXTINCLUDE | ||
34 | +BEGIN | ||
35 | + "#include ""winres.h""\r\n" | ||
36 | + "\0" | ||
37 | +END | ||
38 | + | ||
39 | +3 TEXTINCLUDE | ||
40 | +BEGIN | ||
41 | + "\r\n" | ||
42 | + "\0" | ||
43 | +END | ||
44 | + | ||
45 | +#endif // APSTUDIO_INVOKED | ||
46 | + | ||
47 | + | ||
48 | +///////////////////////////////////////////////////////////////////////////// | ||
49 | +// | ||
50 | +// Icon | ||
51 | +// | ||
52 | + | ||
53 | +// Icon with lowest ID value placed first to ensure application icon | ||
54 | +// remains consistent on all systems. | ||
55 | +IDI_APP_ICON ICON "resources\\app_icon.ico" | ||
56 | + | ||
57 | + | ||
58 | +///////////////////////////////////////////////////////////////////////////// | ||
59 | +// | ||
60 | +// Version | ||
61 | +// | ||
62 | + | ||
63 | +#ifdef FLUTTER_BUILD_NUMBER | ||
64 | +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER | ||
65 | +#else | ||
66 | +#define VERSION_AS_NUMBER 1,0,0 | ||
67 | +#endif | ||
68 | + | ||
69 | +#ifdef FLUTTER_BUILD_NAME | ||
70 | +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME | ||
71 | +#else | ||
72 | +#define VERSION_AS_STRING "1.0.0" | ||
73 | +#endif | ||
74 | + | ||
75 | +VS_VERSION_INFO VERSIONINFO | ||
76 | + FILEVERSION VERSION_AS_NUMBER | ||
77 | + PRODUCTVERSION VERSION_AS_NUMBER | ||
78 | + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK | ||
79 | +#ifdef _DEBUG | ||
80 | + FILEFLAGS VS_FF_DEBUG | ||
81 | +#else | ||
82 | + FILEFLAGS 0x0L | ||
83 | +#endif | ||
84 | + FILEOS VOS__WINDOWS32 | ||
85 | + FILETYPE VFT_APP | ||
86 | + FILESUBTYPE 0x0L | ||
87 | +BEGIN | ||
88 | + BLOCK "StringFileInfo" | ||
89 | + BEGIN | ||
90 | + BLOCK "040904e4" | ||
91 | + BEGIN | ||
92 | + VALUE "CompanyName", "li.zhuoyuan" "\0" | ||
93 | + VALUE "FileDescription", "A new Flutter project." "\0" | ||
94 | + VALUE "FileVersion", VERSION_AS_STRING "\0" | ||
95 | + VALUE "InternalName", "example" "\0" | ||
96 | + VALUE "LegalCopyright", "Copyright (C) 2021 li.zhuoyuan. All rights reserved." "\0" | ||
97 | + VALUE "OriginalFilename", "example.exe" "\0" | ||
98 | + VALUE "ProductName", "example" "\0" | ||
99 | + VALUE "ProductVersion", VERSION_AS_STRING "\0" | ||
100 | + END | ||
101 | + END | ||
102 | + BLOCK "VarFileInfo" | ||
103 | + BEGIN | ||
104 | + VALUE "Translation", 0x409, 1252 | ||
105 | + END | ||
106 | +END | ||
107 | + | ||
108 | +#endif // English (United States) resources | ||
109 | +///////////////////////////////////////////////////////////////////////////// | ||
110 | + | ||
111 | + | ||
112 | + | ||
113 | +#ifndef APSTUDIO_INVOKED | ||
114 | +///////////////////////////////////////////////////////////////////////////// | ||
115 | +// | ||
116 | +// Generated from the TEXTINCLUDE 3 resource. | ||
117 | +// | ||
118 | + | ||
119 | + | ||
120 | +///////////////////////////////////////////////////////////////////////////// | ||
121 | +#endif // not APSTUDIO_INVOKED |
example/windows/runner/flutter_window.cpp
0 → 100644
1 | +#include "flutter_window.h" | ||
2 | + | ||
3 | +#include <optional> | ||
4 | + | ||
5 | +#include "flutter/generated_plugin_registrant.h" | ||
6 | + | ||
7 | +FlutterWindow::FlutterWindow(RunLoop* run_loop, | ||
8 | + const flutter::DartProject& project) | ||
9 | + : run_loop_(run_loop), project_(project) {} | ||
10 | + | ||
11 | +FlutterWindow::~FlutterWindow() {} | ||
12 | + | ||
13 | +bool FlutterWindow::OnCreate() { | ||
14 | + if (!Win32Window::OnCreate()) { | ||
15 | + return false; | ||
16 | + } | ||
17 | + | ||
18 | + RECT frame = GetClientArea(); | ||
19 | + | ||
20 | + // The size here must match the window dimensions to avoid unnecessary surface | ||
21 | + // creation / destruction in the startup path. | ||
22 | + flutter_controller_ = std::make_unique<flutter::FlutterViewController>( | ||
23 | + frame.right - frame.left, frame.bottom - frame.top, project_); | ||
24 | + // Ensure that basic setup of the controller was successful. | ||
25 | + if (!flutter_controller_->engine() || !flutter_controller_->view()) { | ||
26 | + return false; | ||
27 | + } | ||
28 | + RegisterPlugins(flutter_controller_->engine()); | ||
29 | + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); | ||
30 | + SetChildContent(flutter_controller_->view()->GetNativeWindow()); | ||
31 | + return true; | ||
32 | +} | ||
33 | + | ||
34 | +void FlutterWindow::OnDestroy() { | ||
35 | + if (flutter_controller_) { | ||
36 | + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); | ||
37 | + flutter_controller_ = nullptr; | ||
38 | + } | ||
39 | + | ||
40 | + Win32Window::OnDestroy(); | ||
41 | +} | ||
42 | + | ||
43 | +LRESULT | ||
44 | +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, | ||
45 | + WPARAM const wparam, | ||
46 | + LPARAM const lparam) noexcept { | ||
47 | + // Give Flutter, including plugins, an opporutunity to handle window messages. | ||
48 | + if (flutter_controller_) { | ||
49 | + std::optional<LRESULT> result = | ||
50 | + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, | ||
51 | + lparam); | ||
52 | + if (result) { | ||
53 | + return *result; | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + switch (message) { | ||
58 | + case WM_FONTCHANGE: | ||
59 | + flutter_controller_->engine()->ReloadSystemFonts(); | ||
60 | + break; | ||
61 | + } | ||
62 | + | ||
63 | + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); | ||
64 | +} |
example/windows/runner/flutter_window.h
0 → 100644
1 | +#ifndef RUNNER_FLUTTER_WINDOW_H_ | ||
2 | +#define RUNNER_FLUTTER_WINDOW_H_ | ||
3 | + | ||
4 | +#include <flutter/dart_project.h> | ||
5 | +#include <flutter/flutter_view_controller.h> | ||
6 | + | ||
7 | +#include <memory> | ||
8 | + | ||
9 | +#include "run_loop.h" | ||
10 | +#include "win32_window.h" | ||
11 | + | ||
12 | +// A window that does nothing but host a Flutter view. | ||
13 | +class FlutterWindow : public Win32Window { | ||
14 | + public: | ||
15 | + // Creates a new FlutterWindow driven by the |run_loop|, hosting a | ||
16 | + // Flutter view running |project|. | ||
17 | + explicit FlutterWindow(RunLoop* run_loop, | ||
18 | + const flutter::DartProject& project); | ||
19 | + virtual ~FlutterWindow(); | ||
20 | + | ||
21 | + protected: | ||
22 | + // Win32Window: | ||
23 | + bool OnCreate() override; | ||
24 | + void OnDestroy() override; | ||
25 | + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, | ||
26 | + LPARAM const lparam) noexcept override; | ||
27 | + | ||
28 | + private: | ||
29 | + // The run loop driving events for this window. | ||
30 | + RunLoop* run_loop_; | ||
31 | + | ||
32 | + // The project to run. | ||
33 | + flutter::DartProject project_; | ||
34 | + | ||
35 | + // The Flutter instance hosted by this window. | ||
36 | + std::unique_ptr<flutter::FlutterViewController> flutter_controller_; | ||
37 | +}; | ||
38 | + | ||
39 | +#endif // RUNNER_FLUTTER_WINDOW_H_ |
example/windows/runner/main.cpp
0 → 100644
1 | +#include <flutter/dart_project.h> | ||
2 | +#include <flutter/flutter_view_controller.h> | ||
3 | +#include <windows.h> | ||
4 | + | ||
5 | +#include "flutter_window.h" | ||
6 | +#include "run_loop.h" | ||
7 | +#include "utils.h" | ||
8 | + | ||
9 | +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, | ||
10 | + _In_ wchar_t *command_line, _In_ int show_command) { | ||
11 | + // Attach to console when present (e.g., 'flutter run') or create a | ||
12 | + // new console when running with a debugger. | ||
13 | + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { | ||
14 | + CreateAndAttachConsole(); | ||
15 | + } | ||
16 | + | ||
17 | + // Initialize COM, so that it is available for use in the library and/or | ||
18 | + // plugins. | ||
19 | + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); | ||
20 | + | ||
21 | + RunLoop run_loop; | ||
22 | + | ||
23 | + flutter::DartProject project(L"data"); | ||
24 | + | ||
25 | + std::vector<std::string> command_line_arguments = | ||
26 | + GetCommandLineArguments(); | ||
27 | + | ||
28 | + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); | ||
29 | + | ||
30 | + FlutterWindow window(&run_loop, project); | ||
31 | + Win32Window::Point origin(10, 10); | ||
32 | + Win32Window::Size size(1280, 720); | ||
33 | + if (!window.CreateAndShow(L"example", origin, size)) { | ||
34 | + return EXIT_FAILURE; | ||
35 | + } | ||
36 | + window.SetQuitOnClose(true); | ||
37 | + | ||
38 | + run_loop.Run(); | ||
39 | + | ||
40 | + ::CoUninitialize(); | ||
41 | + return EXIT_SUCCESS; | ||
42 | +} |
example/windows/runner/resource.h
0 → 100644
1 | +//{{NO_DEPENDENCIES}} | ||
2 | +// Microsoft Visual C++ generated include file. | ||
3 | +// Used by Runner.rc | ||
4 | +// | ||
5 | +#define IDI_APP_ICON 101 | ||
6 | + | ||
7 | +// Next default values for new objects | ||
8 | +// | ||
9 | +#ifdef APSTUDIO_INVOKED | ||
10 | +#ifndef APSTUDIO_READONLY_SYMBOLS | ||
11 | +#define _APS_NEXT_RESOURCE_VALUE 102 | ||
12 | +#define _APS_NEXT_COMMAND_VALUE 40001 | ||
13 | +#define _APS_NEXT_CONTROL_VALUE 1001 | ||
14 | +#define _APS_NEXT_SYMED_VALUE 101 | ||
15 | +#endif | ||
16 | +#endif |
No preview for this file type
example/windows/runner/run_loop.cpp
0 → 100644
1 | +#include "run_loop.h" | ||
2 | + | ||
3 | +#include <windows.h> | ||
4 | + | ||
5 | +#include <algorithm> | ||
6 | + | ||
7 | +RunLoop::RunLoop() {} | ||
8 | + | ||
9 | +RunLoop::~RunLoop() {} | ||
10 | + | ||
11 | +void RunLoop::Run() { | ||
12 | + bool keep_running = true; | ||
13 | + TimePoint next_flutter_event_time = TimePoint::clock::now(); | ||
14 | + while (keep_running) { | ||
15 | + std::chrono::nanoseconds wait_duration = | ||
16 | + std::max(std::chrono::nanoseconds(0), | ||
17 | + next_flutter_event_time - TimePoint::clock::now()); | ||
18 | + ::MsgWaitForMultipleObjects( | ||
19 | + 0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000), | ||
20 | + QS_ALLINPUT); | ||
21 | + bool processed_events = false; | ||
22 | + MSG message; | ||
23 | + // All pending Windows messages must be processed; MsgWaitForMultipleObjects | ||
24 | + // won't return again for items left in the queue after PeekMessage. | ||
25 | + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { | ||
26 | + processed_events = true; | ||
27 | + if (message.message == WM_QUIT) { | ||
28 | + keep_running = false; | ||
29 | + break; | ||
30 | + } | ||
31 | + ::TranslateMessage(&message); | ||
32 | + ::DispatchMessage(&message); | ||
33 | + // Allow Flutter to process messages each time a Windows message is | ||
34 | + // processed, to prevent starvation. | ||
35 | + next_flutter_event_time = | ||
36 | + std::min(next_flutter_event_time, ProcessFlutterMessages()); | ||
37 | + } | ||
38 | + // If the PeekMessage loop didn't run, process Flutter messages. | ||
39 | + if (!processed_events) { | ||
40 | + next_flutter_event_time = | ||
41 | + std::min(next_flutter_event_time, ProcessFlutterMessages()); | ||
42 | + } | ||
43 | + } | ||
44 | +} | ||
45 | + | ||
46 | +void RunLoop::RegisterFlutterInstance( | ||
47 | + flutter::FlutterEngine* flutter_instance) { | ||
48 | + flutter_instances_.insert(flutter_instance); | ||
49 | +} | ||
50 | + | ||
51 | +void RunLoop::UnregisterFlutterInstance( | ||
52 | + flutter::FlutterEngine* flutter_instance) { | ||
53 | + flutter_instances_.erase(flutter_instance); | ||
54 | +} | ||
55 | + | ||
56 | +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { | ||
57 | + TimePoint next_event_time = TimePoint::max(); | ||
58 | + for (auto instance : flutter_instances_) { | ||
59 | + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); | ||
60 | + if (wait_duration != std::chrono::nanoseconds::max()) { | ||
61 | + next_event_time = | ||
62 | + std::min(next_event_time, TimePoint::clock::now() + wait_duration); | ||
63 | + } | ||
64 | + } | ||
65 | + return next_event_time; | ||
66 | +} |
example/windows/runner/run_loop.h
0 → 100644
1 | +#ifndef RUNNER_RUN_LOOP_H_ | ||
2 | +#define RUNNER_RUN_LOOP_H_ | ||
3 | + | ||
4 | +#include <flutter/flutter_engine.h> | ||
5 | + | ||
6 | +#include <chrono> | ||
7 | +#include <set> | ||
8 | + | ||
9 | +// A runloop that will service events for Flutter instances as well | ||
10 | +// as native messages. | ||
11 | +class RunLoop { | ||
12 | + public: | ||
13 | + RunLoop(); | ||
14 | + ~RunLoop(); | ||
15 | + | ||
16 | + // Prevent copying | ||
17 | + RunLoop(RunLoop const&) = delete; | ||
18 | + RunLoop& operator=(RunLoop const&) = delete; | ||
19 | + | ||
20 | + // Runs the run loop until the application quits. | ||
21 | + void Run(); | ||
22 | + | ||
23 | + // Registers the given Flutter instance for event servicing. | ||
24 | + void RegisterFlutterInstance( | ||
25 | + flutter::FlutterEngine* flutter_instance); | ||
26 | + | ||
27 | + // Unregisters the given Flutter instance from event servicing. | ||
28 | + void UnregisterFlutterInstance( | ||
29 | + flutter::FlutterEngine* flutter_instance); | ||
30 | + | ||
31 | + private: | ||
32 | + using TimePoint = std::chrono::steady_clock::time_point; | ||
33 | + | ||
34 | + // Processes all currently pending messages for registered Flutter instances. | ||
35 | + TimePoint ProcessFlutterMessages(); | ||
36 | + | ||
37 | + std::set<flutter::FlutterEngine*> flutter_instances_; | ||
38 | +}; | ||
39 | + | ||
40 | +#endif // RUNNER_RUN_LOOP_H_ |
example/windows/runner/runner.exe.manifest
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
2 | +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
3 | + <application xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
4 | + <windowsSettings> | ||
5 | + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> | ||
6 | + </windowsSettings> | ||
7 | + </application> | ||
8 | + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
9 | + <application> | ||
10 | + <!-- Windows 10 --> | ||
11 | + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | ||
12 | + <!-- Windows 8.1 --> | ||
13 | + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
14 | + <!-- Windows 8 --> | ||
15 | + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
16 | + <!-- Windows 7 --> | ||
17 | + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
18 | + </application> | ||
19 | + </compatibility> | ||
20 | +</assembly> |
example/windows/runner/utils.cpp
0 → 100644
1 | +#include "utils.h" | ||
2 | + | ||
3 | +#include <flutter_windows.h> | ||
4 | +#include <io.h> | ||
5 | +#include <stdio.h> | ||
6 | +#include <windows.h> | ||
7 | + | ||
8 | +#include <iostream> | ||
9 | + | ||
10 | +void CreateAndAttachConsole() { | ||
11 | + if (::AllocConsole()) { | ||
12 | + FILE *unused; | ||
13 | + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { | ||
14 | + _dup2(_fileno(stdout), 1); | ||
15 | + } | ||
16 | + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { | ||
17 | + _dup2(_fileno(stdout), 2); | ||
18 | + } | ||
19 | + std::ios::sync_with_stdio(); | ||
20 | + FlutterDesktopResyncOutputStreams(); | ||
21 | + } | ||
22 | +} | ||
23 | + | ||
24 | +std::vector<std::string> GetCommandLineArguments() { | ||
25 | + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. | ||
26 | + int argc; | ||
27 | + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); | ||
28 | + if (argv == nullptr) { | ||
29 | + return std::vector<std::string>(); | ||
30 | + } | ||
31 | + | ||
32 | + std::vector<std::string> command_line_arguments; | ||
33 | + | ||
34 | + // Skip the first argument as it's the binary name. | ||
35 | + for (int i = 1; i < argc; i++) { | ||
36 | + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); | ||
37 | + } | ||
38 | + | ||
39 | + ::LocalFree(argv); | ||
40 | + | ||
41 | + return command_line_arguments; | ||
42 | +} | ||
43 | + | ||
44 | +std::string Utf8FromUtf16(const wchar_t* utf16_string) { | ||
45 | + if (utf16_string == nullptr) { | ||
46 | + return std::string(); | ||
47 | + } | ||
48 | + int target_length = ::WideCharToMultiByte( | ||
49 | + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, | ||
50 | + -1, nullptr, 0, nullptr, nullptr); | ||
51 | + if (target_length == 0) { | ||
52 | + return std::string(); | ||
53 | + } | ||
54 | + std::string utf8_string; | ||
55 | + utf8_string.resize(target_length); | ||
56 | + int converted_length = ::WideCharToMultiByte( | ||
57 | + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, | ||
58 | + -1, utf8_string.data(), | ||
59 | + target_length, nullptr, nullptr); | ||
60 | + if (converted_length == 0) { | ||
61 | + return std::string(); | ||
62 | + } | ||
63 | + return utf8_string; | ||
64 | +} |
example/windows/runner/utils.h
0 → 100644
1 | +#ifndef RUNNER_UTILS_H_ | ||
2 | +#define RUNNER_UTILS_H_ | ||
3 | + | ||
4 | +#include <string> | ||
5 | +#include <vector> | ||
6 | + | ||
7 | +// Creates a console for the process, and redirects stdout and stderr to | ||
8 | +// it for both the runner and the Flutter library. | ||
9 | +void CreateAndAttachConsole(); | ||
10 | + | ||
11 | +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string | ||
12 | +// encoded in UTF-8. Returns an empty std::string on failure. | ||
13 | +std::string Utf8FromUtf16(const wchar_t* utf16_string); | ||
14 | + | ||
15 | +// Gets the command line arguments passed in as a std::vector<std::string>, | ||
16 | +// encoded in UTF-8. Returns an empty std::vector<std::string> on failure. | ||
17 | +std::vector<std::string> GetCommandLineArguments(); | ||
18 | + | ||
19 | +#endif // RUNNER_UTILS_H_ |
example/windows/runner/win32_window.cpp
0 → 100644
1 | +#include "win32_window.h" | ||
2 | + | ||
3 | +#include <flutter_windows.h> | ||
4 | + | ||
5 | +#include "resource.h" | ||
6 | + | ||
7 | +namespace { | ||
8 | + | ||
9 | +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; | ||
10 | + | ||
11 | +// The number of Win32Window objects that currently exist. | ||
12 | +static int g_active_window_count = 0; | ||
13 | + | ||
14 | +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); | ||
15 | + | ||
16 | +// Scale helper to convert logical scaler values to physical using passed in | ||
17 | +// scale factor | ||
18 | +int Scale(int source, double scale_factor) { | ||
19 | + return static_cast<int>(source * scale_factor); | ||
20 | +} | ||
21 | + | ||
22 | +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. | ||
23 | +// This API is only needed for PerMonitor V1 awareness mode. | ||
24 | +void EnableFullDpiSupportIfAvailable(HWND hwnd) { | ||
25 | + HMODULE user32_module = LoadLibraryA("User32.dll"); | ||
26 | + if (!user32_module) { | ||
27 | + return; | ||
28 | + } | ||
29 | + auto enable_non_client_dpi_scaling = | ||
30 | + reinterpret_cast<EnableNonClientDpiScaling*>( | ||
31 | + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); | ||
32 | + if (enable_non_client_dpi_scaling != nullptr) { | ||
33 | + enable_non_client_dpi_scaling(hwnd); | ||
34 | + FreeLibrary(user32_module); | ||
35 | + } | ||
36 | +} | ||
37 | + | ||
38 | +} // namespace | ||
39 | + | ||
40 | +// Manages the Win32Window's window class registration. | ||
41 | +class WindowClassRegistrar { | ||
42 | + public: | ||
43 | + ~WindowClassRegistrar() = default; | ||
44 | + | ||
45 | + // Returns the singleton registar instance. | ||
46 | + static WindowClassRegistrar* GetInstance() { | ||
47 | + if (!instance_) { | ||
48 | + instance_ = new WindowClassRegistrar(); | ||
49 | + } | ||
50 | + return instance_; | ||
51 | + } | ||
52 | + | ||
53 | + // Returns the name of the window class, registering the class if it hasn't | ||
54 | + // previously been registered. | ||
55 | + const wchar_t* GetWindowClass(); | ||
56 | + | ||
57 | + // Unregisters the window class. Should only be called if there are no | ||
58 | + // instances of the window. | ||
59 | + void UnregisterWindowClass(); | ||
60 | + | ||
61 | + private: | ||
62 | + WindowClassRegistrar() = default; | ||
63 | + | ||
64 | + static WindowClassRegistrar* instance_; | ||
65 | + | ||
66 | + bool class_registered_ = false; | ||
67 | +}; | ||
68 | + | ||
69 | +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; | ||
70 | + | ||
71 | +const wchar_t* WindowClassRegistrar::GetWindowClass() { | ||
72 | + if (!class_registered_) { | ||
73 | + WNDCLASS window_class{}; | ||
74 | + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); | ||
75 | + window_class.lpszClassName = kWindowClassName; | ||
76 | + window_class.style = CS_HREDRAW | CS_VREDRAW; | ||
77 | + window_class.cbClsExtra = 0; | ||
78 | + window_class.cbWndExtra = 0; | ||
79 | + window_class.hInstance = GetModuleHandle(nullptr); | ||
80 | + window_class.hIcon = | ||
81 | + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); | ||
82 | + window_class.hbrBackground = 0; | ||
83 | + window_class.lpszMenuName = nullptr; | ||
84 | + window_class.lpfnWndProc = Win32Window::WndProc; | ||
85 | + RegisterClass(&window_class); | ||
86 | + class_registered_ = true; | ||
87 | + } | ||
88 | + return kWindowClassName; | ||
89 | +} | ||
90 | + | ||
91 | +void WindowClassRegistrar::UnregisterWindowClass() { | ||
92 | + UnregisterClass(kWindowClassName, nullptr); | ||
93 | + class_registered_ = false; | ||
94 | +} | ||
95 | + | ||
96 | +Win32Window::Win32Window() { | ||
97 | + ++g_active_window_count; | ||
98 | +} | ||
99 | + | ||
100 | +Win32Window::~Win32Window() { | ||
101 | + --g_active_window_count; | ||
102 | + Destroy(); | ||
103 | +} | ||
104 | + | ||
105 | +bool Win32Window::CreateAndShow(const std::wstring& title, | ||
106 | + const Point& origin, | ||
107 | + const Size& size) { | ||
108 | + Destroy(); | ||
109 | + | ||
110 | + const wchar_t* window_class = | ||
111 | + WindowClassRegistrar::GetInstance()->GetWindowClass(); | ||
112 | + | ||
113 | + const POINT target_point = {static_cast<LONG>(origin.x), | ||
114 | + static_cast<LONG>(origin.y)}; | ||
115 | + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); | ||
116 | + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); | ||
117 | + double scale_factor = dpi / 96.0; | ||
118 | + | ||
119 | + HWND window = CreateWindow( | ||
120 | + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, | ||
121 | + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), | ||
122 | + Scale(size.width, scale_factor), Scale(size.height, scale_factor), | ||
123 | + nullptr, nullptr, GetModuleHandle(nullptr), this); | ||
124 | + | ||
125 | + if (!window) { | ||
126 | + return false; | ||
127 | + } | ||
128 | + | ||
129 | + return OnCreate(); | ||
130 | +} | ||
131 | + | ||
132 | +// static | ||
133 | +LRESULT CALLBACK Win32Window::WndProc(HWND const window, | ||
134 | + UINT const message, | ||
135 | + WPARAM const wparam, | ||
136 | + LPARAM const lparam) noexcept { | ||
137 | + if (message == WM_NCCREATE) { | ||
138 | + auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam); | ||
139 | + SetWindowLongPtr(window, GWLP_USERDATA, | ||
140 | + reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams)); | ||
141 | + | ||
142 | + auto that = static_cast<Win32Window*>(window_struct->lpCreateParams); | ||
143 | + EnableFullDpiSupportIfAvailable(window); | ||
144 | + that->window_handle_ = window; | ||
145 | + } else if (Win32Window* that = GetThisFromHandle(window)) { | ||
146 | + return that->MessageHandler(window, message, wparam, lparam); | ||
147 | + } | ||
148 | + | ||
149 | + return DefWindowProc(window, message, wparam, lparam); | ||
150 | +} | ||
151 | + | ||
152 | +LRESULT | ||
153 | +Win32Window::MessageHandler(HWND hwnd, | ||
154 | + UINT const message, | ||
155 | + WPARAM const wparam, | ||
156 | + LPARAM const lparam) noexcept { | ||
157 | + switch (message) { | ||
158 | + case WM_DESTROY: | ||
159 | + window_handle_ = nullptr; | ||
160 | + Destroy(); | ||
161 | + if (quit_on_close_) { | ||
162 | + PostQuitMessage(0); | ||
163 | + } | ||
164 | + return 0; | ||
165 | + | ||
166 | + case WM_DPICHANGED: { | ||
167 | + auto newRectSize = reinterpret_cast<RECT*>(lparam); | ||
168 | + LONG newWidth = newRectSize->right - newRectSize->left; | ||
169 | + LONG newHeight = newRectSize->bottom - newRectSize->top; | ||
170 | + | ||
171 | + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, | ||
172 | + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); | ||
173 | + | ||
174 | + return 0; | ||
175 | + } | ||
176 | + case WM_SIZE: { | ||
177 | + RECT rect = GetClientArea(); | ||
178 | + if (child_content_ != nullptr) { | ||
179 | + // Size and position the child window. | ||
180 | + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, | ||
181 | + rect.bottom - rect.top, TRUE); | ||
182 | + } | ||
183 | + return 0; | ||
184 | + } | ||
185 | + | ||
186 | + case WM_ACTIVATE: | ||
187 | + if (child_content_ != nullptr) { | ||
188 | + SetFocus(child_content_); | ||
189 | + } | ||
190 | + return 0; | ||
191 | + } | ||
192 | + | ||
193 | + return DefWindowProc(window_handle_, message, wparam, lparam); | ||
194 | +} | ||
195 | + | ||
196 | +void Win32Window::Destroy() { | ||
197 | + OnDestroy(); | ||
198 | + | ||
199 | + if (window_handle_) { | ||
200 | + DestroyWindow(window_handle_); | ||
201 | + window_handle_ = nullptr; | ||
202 | + } | ||
203 | + if (g_active_window_count == 0) { | ||
204 | + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); | ||
205 | + } | ||
206 | +} | ||
207 | + | ||
208 | +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { | ||
209 | + return reinterpret_cast<Win32Window*>( | ||
210 | + GetWindowLongPtr(window, GWLP_USERDATA)); | ||
211 | +} | ||
212 | + | ||
213 | +void Win32Window::SetChildContent(HWND content) { | ||
214 | + child_content_ = content; | ||
215 | + SetParent(content, window_handle_); | ||
216 | + RECT frame = GetClientArea(); | ||
217 | + | ||
218 | + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, | ||
219 | + frame.bottom - frame.top, true); | ||
220 | + | ||
221 | + SetFocus(child_content_); | ||
222 | +} | ||
223 | + | ||
224 | +RECT Win32Window::GetClientArea() { | ||
225 | + RECT frame; | ||
226 | + GetClientRect(window_handle_, &frame); | ||
227 | + return frame; | ||
228 | +} | ||
229 | + | ||
230 | +HWND Win32Window::GetHandle() { | ||
231 | + return window_handle_; | ||
232 | +} | ||
233 | + | ||
234 | +void Win32Window::SetQuitOnClose(bool quit_on_close) { | ||
235 | + quit_on_close_ = quit_on_close; | ||
236 | +} | ||
237 | + | ||
238 | +bool Win32Window::OnCreate() { | ||
239 | + // No-op; provided for subclasses. | ||
240 | + return true; | ||
241 | +} | ||
242 | + | ||
243 | +void Win32Window::OnDestroy() { | ||
244 | + // No-op; provided for subclasses. | ||
245 | +} |
example/windows/runner/win32_window.h
0 → 100644
1 | +#ifndef RUNNER_WIN32_WINDOW_H_ | ||
2 | +#define RUNNER_WIN32_WINDOW_H_ | ||
3 | + | ||
4 | +#include <windows.h> | ||
5 | + | ||
6 | +#include <functional> | ||
7 | +#include <memory> | ||
8 | +#include <string> | ||
9 | + | ||
10 | +// A class abstraction for a high DPI-aware Win32 Window. Intended to be | ||
11 | +// inherited from by classes that wish to specialize with custom | ||
12 | +// rendering and input handling | ||
13 | +class Win32Window { | ||
14 | + public: | ||
15 | + struct Point { | ||
16 | + unsigned int x; | ||
17 | + unsigned int y; | ||
18 | + Point(unsigned int x, unsigned int y) : x(x), y(y) {} | ||
19 | + }; | ||
20 | + | ||
21 | + struct Size { | ||
22 | + unsigned int width; | ||
23 | + unsigned int height; | ||
24 | + Size(unsigned int width, unsigned int height) | ||
25 | + : width(width), height(height) {} | ||
26 | + }; | ||
27 | + | ||
28 | + Win32Window(); | ||
29 | + virtual ~Win32Window(); | ||
30 | + | ||
31 | + // Creates and shows a win32 window with |title| and position and size using | ||
32 | + // |origin| and |size|. New windows are created on the default monitor. Window | ||
33 | + // sizes are specified to the OS in physical pixels, hence to ensure a | ||
34 | + // consistent size to will treat the width height passed in to this function | ||
35 | + // as logical pixels and scale to appropriate for the default monitor. Returns | ||
36 | + // true if the window was created successfully. | ||
37 | + bool CreateAndShow(const std::wstring& title, | ||
38 | + const Point& origin, | ||
39 | + const Size& size); | ||
40 | + | ||
41 | + // Release OS resources associated with window. | ||
42 | + void Destroy(); | ||
43 | + | ||
44 | + // Inserts |content| into the window tree. | ||
45 | + void SetChildContent(HWND content); | ||
46 | + | ||
47 | + // Returns the backing Window handle to enable clients to set icon and other | ||
48 | + // window properties. Returns nullptr if the window has been destroyed. | ||
49 | + HWND GetHandle(); | ||
50 | + | ||
51 | + // If true, closing this window will quit the application. | ||
52 | + void SetQuitOnClose(bool quit_on_close); | ||
53 | + | ||
54 | + // Return a RECT representing the bounds of the current client area. | ||
55 | + RECT GetClientArea(); | ||
56 | + | ||
57 | + protected: | ||
58 | + // Processes and route salient window messages for mouse handling, | ||
59 | + // size change and DPI. Delegates handling of these to member overloads that | ||
60 | + // inheriting classes can handle. | ||
61 | + virtual LRESULT MessageHandler(HWND window, | ||
62 | + UINT const message, | ||
63 | + WPARAM const wparam, | ||
64 | + LPARAM const lparam) noexcept; | ||
65 | + | ||
66 | + // Called when CreateAndShow is called, allowing subclass window-related | ||
67 | + // setup. Subclasses should return false if setup fails. | ||
68 | + virtual bool OnCreate(); | ||
69 | + | ||
70 | + // Called when Destroy is called. | ||
71 | + virtual void OnDestroy(); | ||
72 | + | ||
73 | + private: | ||
74 | + friend class WindowClassRegistrar; | ||
75 | + | ||
76 | + // OS callback called by message pump. Handles the WM_NCCREATE message which | ||
77 | + // is passed when the non-client area is being created and enables automatic | ||
78 | + // non-client DPI scaling so that the non-client area automatically | ||
79 | + // responsponds to changes in DPI. All other messages are handled by | ||
80 | + // MessageHandler. | ||
81 | + static LRESULT CALLBACK WndProc(HWND const window, | ||
82 | + UINT const message, | ||
83 | + WPARAM const wparam, | ||
84 | + LPARAM const lparam) noexcept; | ||
85 | + | ||
86 | + // Retrieves a class instance pointer for |window| | ||
87 | + static Win32Window* GetThisFromHandle(HWND const window) noexcept; | ||
88 | + | ||
89 | + bool quit_on_close_ = false; | ||
90 | + | ||
91 | + // window handle for top level window. | ||
92 | + HWND window_handle_ = nullptr; | ||
93 | + | ||
94 | + // window handle for hosted content. | ||
95 | + HWND child_content_ = nullptr; | ||
96 | +}; | ||
97 | + | ||
98 | +#endif // RUNNER_WIN32_WINDOW_H_ |
-
Please register or login to post a comment