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