李卓原

example update

@@ -5,155 +5,3 @@ @@ -5,155 +5,3 @@
5 5
6 ![平板效果](../demo_tablet_zh.png) 6 ![平板效果](../demo_tablet_zh.png)
7 ![tablet effect](../demo_tablet_en.png) 7 ![tablet effect](../demo_tablet_en.png)
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>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>IDEDidComputeMac32BitWarning</key>
  6 + <true/>
  7 +</dict>
  8 +</plist>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>PreviewsEnabled</key>
  6 + <false/>
  7 +</dict>
  8 +</plist>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>PreviewsEnabled</key>
  6 + <false/>
  7 +</dict>
  8 +</plist>
  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 +}
  1 +#import "GeneratedPluginRegistrant.h"
  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 +}
  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>
  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 +}
  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/
  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)
  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 +#include "generated_plugin_registrant.h"
  6 +
  7 +
  8 +void RegisterPlugins(flutter::PluginRegistry* registry) {
  9 +}
  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)
  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)
  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
  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 +}
  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_
  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 +}
  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
  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 +}
  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_
  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>
  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 +}
  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_
  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 +}
  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_