diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
new file mode 100644
index 0000000..26f7e43
--- /dev/null
+++ b/example/analysis_options.yaml
@@ -0,0 +1,3 @@
+analyzer:
+ errors:
+ todo: ignore
diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/lib/demos/expander_fab_menu/expander_fab_menu.dart b/example/lib/demos/expander_fab_menu/expander_fab_menu.dart
index bb9931a..4672c6a 100644
--- a/example/lib/demos/expander_fab_menu/expander_fab_menu.dart
+++ b/example/lib/demos/expander_fab_menu/expander_fab_menu.dart
@@ -21,6 +21,7 @@ class ExpanderFabMenu extends StatelessWidget {
Icons.arrow_back,
color: Colors.black,
),
+ onPressed: () {},
),
),
body: MyMenu(
@@ -61,7 +62,6 @@ class _MyMenuState extends State with TickerProviderStateMixin {
@override
void initState() {
super.initState();
- final dur = Duration(seconds: 1);
// anim = AnimationController(
// vsync: this,
// duration: dur,
@@ -173,12 +173,10 @@ class MyCoolMenuScene extends GSprite {
button = MyButton();
}
- GRect _position;
double buttonY;
GShape curtain;
void updatePosition(GRect position) {
- _position = position;
button.x = position.x + position.width / 2;
button.y = buttonY = position.y + position.height / 2;
}
@@ -220,7 +218,6 @@ class MyCoolMenuScene extends GSprite {
}
},
);
-
} else {
curtain.tween(
duration: .5,
@@ -288,22 +285,22 @@ class MyCoolMenuScene extends GSprite {
doc: menuContainer,
);
itm.alignPivot();
- itm.alpha=0;
+ itm.alpha = 0;
itm.y = i * 34.0;
items.add(itm);
}
menuContainer.alignPivot();
- menuContainer.setPosition(sw/2,sh/2);
+ menuContainer.setPosition(sw / 2, sh / 2);
}
void showMenuNow() {
- var len = items.length ;
+ var len = items.length;
for (var i = 0; i < items.length; ++i) {
var itm = items[i];
itm.y = i * 34.0;
- double ta = isOpen ? 1 : 0 ;
- if( isOpen ){
- itm.tween(duration: .45, delay: .25 + ((len-1)-i) * .09, alpha: ta);
+ double ta = isOpen ? 1 : 0;
+ if (isOpen) {
+ itm.tween(duration: .45, delay: .25 + ((len - 1) - i) * .09, alpha: ta);
} else {
itm.tween(duration: .12, delay: 0, alpha: 0, overwrite: 1);
}
diff --git a/example/lib/demos/path_chart_stress/scene.dart b/example/lib/demos/path_chart_stress/scene.dart
index abc4e92..cf80996 100644
--- a/example/lib/demos/path_chart_stress/scene.dart
+++ b/example/lib/demos/path_chart_stress/scene.dart
@@ -28,8 +28,6 @@ class PathChartScene extends GSprite {
container.x = stage.stageWidth * .8;
container.y = stage.stageHeight * .5;
- var scaleR = 0.0;
- var tw = container.x;
stage.onEnterFrame.add((event) {
// if (container.width > container.x + 100) {
// tw = container.x + 100;
@@ -87,8 +85,6 @@ class ChartSection extends GShape {
stage.onEnterFrame.add((event) {
if (++frameCount % 3 == 0) {
addSlot();
- var maxPos = stage.stageWidth * .7;
- var tempW = width;
pivotX = width;
// x = maxPos;
// if (tempW > maxPos) {
@@ -112,31 +108,6 @@ class ChartSection extends GShape {
void addSlot() {
++numQuads;
label.text = '$numQuads rects';
- var rects = List.generate(1, (index) {
- var pw = Math.randomRange(20, 40);
- var ph = Math.randomRange(20, 80);
- var py = Math.randomRange(
- lastRect.y - ph / 2, lastRect.y + lastRect.height / 2);
- final rect = GRect(lastRect.right, py, pw, ph);
- myPath = Path.combine(
- PathOperation.union,
- myPath,
- Path()..addRect(rect.toNative()),
- );
-
- outputPath = myPath;
-
- // outputPath = Path.combine(
- // PathOperation.intersect,
- // outputPath,
- // maskPath,
- // );
-
- // lastX += pw;
- lastRect = rect;
- return rect;
- });
-
graphics.clear();
graphics.beginFill(color.withOpacity(.3));
graphics.lineStyle(2, color);
diff --git a/example/linux/.gitignore b/example/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/example/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
new file mode 100644
index 0000000..290c3e8
--- /dev/null
+++ b/example/linux/CMakeLists.txt
@@ -0,0 +1,106 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "example")
+set(APPLICATION_ID "com.example.example")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..a1da1b9
--- /dev/null
+++ b/example/linux/flutter/CMakeLists.txt
@@ -0,0 +1,91 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
+pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+ PkgConfig::BLKID
+ PkgConfig::LZMA
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ linux-x64 ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..d38195a
--- /dev/null
+++ b/example/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,9 @@
+//
+// Generated file. Do not edit.
+//
+
+#include "generated_plugin_registrant.h"
+
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+}
diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..9bf7478
--- /dev/null
+++ b/example/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,13 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..51436ae
--- /dev/null
+++ b/example/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/example/linux/main.cc b/example/linux/main.cc
new file mode 100644
index 0000000..e7c5c54
--- /dev/null
+++ b/example/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc
new file mode 100644
index 0000000..543eaca
--- /dev/null
+++ b/example/linux/my_application.cc
@@ -0,0 +1,104 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen *screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "example");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ }
+ else {
+ gtk_window_set_title(window, "example");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject *object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ nullptr));
+}
diff --git a/example/linux/my_application.h b/example/linux/my_application.h
new file mode 100644
index 0000000..72271d5
--- /dev/null
+++ b/example/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 10259a4..0ffa6c9 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -12,7 +12,7 @@ dependencies:
flutter:
sdk: flutter
flutter_icons: ^1.1.0
- flutter_svg: 0.18.1
+ flutter_svg: ^0.21.0-nullsafety.0
graphx:
path: ../
dev_dependencies:
@@ -39,4 +39,4 @@ flutter:
fonts:
- family: pressstart
fonts:
- - asset: assets/fonts/pressstart.ttf
\ No newline at end of file
+ - asset: assets/fonts/pressstart.ttf
diff --git a/example/windows/.gitignore b/example/windows/.gitignore
new file mode 100644
index 0000000..d492d0d
--- /dev/null
+++ b/example/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt
new file mode 100644
index 0000000..abf9040
--- /dev/null
+++ b/example/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(example LANGUAGES CXX)
+
+set(BINARY_NAME "example")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000..b02c548
--- /dev/null
+++ b/example/windows/flutter/CMakeLists.txt
@@ -0,0 +1,103 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..4bfa0f3
--- /dev/null
+++ b/example/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,9 @@
+//
+// Generated file. Do not edit.
+//
+
+#include "generated_plugin_registrant.h"
+
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {
+}
diff --git a/example/windows/flutter/generated_plugin_registrant.h b/example/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..9846246
--- /dev/null
+++ b/example/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,13 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..4d10c25
--- /dev/null
+++ b/example/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000..977e38b
--- /dev/null
+++ b/example/windows/runner/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "run_loop.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc
new file mode 100644
index 0000000..51812dc
--- /dev/null
+++ b/example/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "com.example" "\0"
+ VALUE "FileDescription", "A new Flutter project." "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "example" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0"
+ VALUE "OriginalFilename", "example.exe" "\0"
+ VALUE "ProductName", "example" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000..c422723
--- /dev/null
+++ b/example/windows/runner/flutter_window.cpp
@@ -0,0 +1,64 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(RunLoop* run_loop,
+ const flutter::DartProject& project)
+ : run_loop_(run_loop), project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opporutunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/example/windows/runner/flutter_window.h b/example/windows/runner/flutter_window.h
new file mode 100644
index 0000000..b663ddd
--- /dev/null
+++ b/example/windows/runner/flutter_window.h
@@ -0,0 +1,39 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "run_loop.h"
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow driven by the |run_loop|, hosting a
+ // Flutter view running |project|.
+ explicit FlutterWindow(RunLoop* run_loop,
+ const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The run loop driving events for this window.
+ RunLoop* run_loop_;
+
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp
new file mode 100644
index 0000000..b637809
--- /dev/null
+++ b/example/windows/runner/main.cpp
@@ -0,0 +1,42 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "run_loop.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ RunLoop run_loop;
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments =
+ GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(&run_loop, project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"example", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ run_loop.Run();
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/example/windows/runner/resource.h b/example/windows/runner/resource.h
new file mode 100644
index 0000000..66a65d1
--- /dev/null
+++ b/example/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/example/windows/runner/resources/app_icon.ico b/example/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000..c04e20c
Binary files /dev/null and b/example/windows/runner/resources/app_icon.ico differ
diff --git a/example/windows/runner/run_loop.cpp b/example/windows/runner/run_loop.cpp
new file mode 100644
index 0000000..2d6636a
--- /dev/null
+++ b/example/windows/runner/run_loop.cpp
@@ -0,0 +1,66 @@
+#include "run_loop.h"
+
+#include
+
+#include
+
+RunLoop::RunLoop() {}
+
+RunLoop::~RunLoop() {}
+
+void RunLoop::Run() {
+ bool keep_running = true;
+ TimePoint next_flutter_event_time = TimePoint::clock::now();
+ while (keep_running) {
+ std::chrono::nanoseconds wait_duration =
+ std::max(std::chrono::nanoseconds(0),
+ next_flutter_event_time - TimePoint::clock::now());
+ ::MsgWaitForMultipleObjects(
+ 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000),
+ QS_ALLINPUT);
+ bool processed_events = false;
+ MSG message;
+ // All pending Windows messages must be processed; MsgWaitForMultipleObjects
+ // won't return again for items left in the queue after PeekMessage.
+ while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
+ processed_events = true;
+ if (message.message == WM_QUIT) {
+ keep_running = false;
+ break;
+ }
+ ::TranslateMessage(&message);
+ ::DispatchMessage(&message);
+ // Allow Flutter to process messages each time a Windows message is
+ // processed, to prevent starvation.
+ next_flutter_event_time =
+ std::min(next_flutter_event_time, ProcessFlutterMessages());
+ }
+ // If the PeekMessage loop didn't run, process Flutter messages.
+ if (!processed_events) {
+ next_flutter_event_time =
+ std::min(next_flutter_event_time, ProcessFlutterMessages());
+ }
+ }
+}
+
+void RunLoop::RegisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance) {
+ flutter_instances_.insert(flutter_instance);
+}
+
+void RunLoop::UnregisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance) {
+ flutter_instances_.erase(flutter_instance);
+}
+
+RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
+ TimePoint next_event_time = TimePoint::max();
+ for (auto instance : flutter_instances_) {
+ std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
+ if (wait_duration != std::chrono::nanoseconds::max()) {
+ next_event_time =
+ std::min(next_event_time, TimePoint::clock::now() + wait_duration);
+ }
+ }
+ return next_event_time;
+}
diff --git a/example/windows/runner/run_loop.h b/example/windows/runner/run_loop.h
new file mode 100644
index 0000000..000d362
--- /dev/null
+++ b/example/windows/runner/run_loop.h
@@ -0,0 +1,40 @@
+#ifndef RUNNER_RUN_LOOP_H_
+#define RUNNER_RUN_LOOP_H_
+
+#include
+
+#include
+#include
+
+// A runloop that will service events for Flutter instances as well
+// as native messages.
+class RunLoop {
+ public:
+ RunLoop();
+ ~RunLoop();
+
+ // Prevent copying
+ RunLoop(RunLoop const&) = delete;
+ RunLoop& operator=(RunLoop const&) = delete;
+
+ // Runs the run loop until the application quits.
+ void Run();
+
+ // Registers the given Flutter instance for event servicing.
+ void RegisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance);
+
+ // Unregisters the given Flutter instance from event servicing.
+ void UnregisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance);
+
+ private:
+ using TimePoint = std::chrono::steady_clock::time_point;
+
+ // Processes all currently pending messages for registered Flutter instances.
+ TimePoint ProcessFlutterMessages();
+
+ std::set flutter_instances_;
+};
+
+#endif // RUNNER_RUN_LOOP_H_
diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000..c977c4a
--- /dev/null
+++ b/example/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp
new file mode 100644
index 0000000..d19bdbb
--- /dev/null
+++ b/example/windows/runner/utils.cpp
@@ -0,0 +1,64 @@
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE *unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector();
+ }
+
+ std::vector command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/example/windows/runner/utils.h b/example/windows/runner/utils.h
new file mode 100644
index 0000000..3879d54
--- /dev/null
+++ b/example/windows/runner/utils.h
@@ -0,0 +1,19 @@
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include
+#include
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector,
+// encoded in UTF-8. Returns an empty std::vector on failure.
+std::vector GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp
new file mode 100644
index 0000000..c10f08d
--- /dev/null
+++ b/example/windows/runner/win32_window.cpp
@@ -0,0 +1,245 @@
+#include "win32_window.h"
+
+#include
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() {
+ ++g_active_window_count;
+}
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title,
+ const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast(origin.x),
+ static_cast(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast(window_struct->lpCreateParams));
+
+ auto that = static_cast(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() {
+ return window_handle_;
+}
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h
new file mode 100644
index 0000000..17ba431
--- /dev/null
+++ b/example/windows/runner/win32_window.h
@@ -0,0 +1,98 @@
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include
+
+#include
+#include
+#include
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title,
+ const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/lib/src/core/scene_config.dart b/lib/src/core/scene_config.dart
index 2f39727..fa4f497 100644
--- a/lib/src/core/scene_config.dart
+++ b/lib/src/core/scene_config.dart
@@ -51,17 +51,17 @@ class SceneConfig {
/// re-builds the SceneController (the ScenePainter and the scene class).
/// disposes and initializes all the scene.
- bool rebuildOnHotReload;
+ bool? rebuildOnHotReload;
/// If the GraphX [SceneController] will use keyboard events.
- bool useKeyboard;
+ bool? useKeyboard;
/// If this GraphX [SceneController] will use pointer (touch/mouse) events.
- bool usePointer;
+ bool? usePointer;
/// Will be overwritten to `true` if [autoUpdateAndRender] is set on any
/// [ScenePainter] layer.
- bool useTicker;
+ bool? useTicker;
/// See [CustomPaint.willChange].
/// Rendering caching flag.
@@ -71,7 +71,7 @@ class SceneConfig {
/// All these flags overrides the value to `true`:
/// [autoUpdateRender], [useTicker], [usePointer], [useKeyboard]
///
- bool painterWillChange;
+ bool? painterWillChange;
/// Avoids the scene from being disposed by the Widget.
/// Meant to be used with `ScenePainter.useOwnCanvas=true`
@@ -79,7 +79,7 @@ class SceneConfig {
/// Picture (or Image eventually) by other [ScenePainter]s.
/// Warning: Experimental
///
- bool isPersistent = false;
+ bool? isPersistent = false;
/// Default flag to make the engine update() the Stage and all
/// his children (onEnterFrame), needed by [GTween] to run the tweens.
@@ -91,7 +91,7 @@ class SceneConfig {
/// And, unless you don't need external control over the rendering & update
/// pipelines for the scene, or if you use a static scene,
/// you should leave it as `true`.
- bool autoUpdateRender = true;
+ bool? autoUpdateRender = true;
SceneConfig({
this.rebuildOnHotReload = true,
@@ -102,14 +102,19 @@ class SceneConfig {
this.autoUpdateRender = true,
this.painterWillChange,
}) {
- if (autoUpdateRender) useTicker = true;
+ if (autoUpdateRender ?? false) useTicker = true;
}
/// Utility method used by the [SceneBuilderWidget] to set the flag
/// `CustomPaint.willChange`
///
bool painterMightChange() {
- if (useTicker || autoUpdateRender || usePointer || useKeyboard) return true;
+ if ((useTicker ?? false) ||
+ (autoUpdateRender ?? false) ||
+ (usePointer ?? false) ||
+ (useKeyboard ?? false)) {
+ return true;
+ }
return painterWillChange ?? false;
}
}
diff --git a/lib/src/core/scene_controller.dart b/lib/src/core/scene_controller.dart
index 1489392..8271623 100644
--- a/lib/src/core/scene_controller.dart
+++ b/lib/src/core/scene_controller.dart
@@ -19,14 +19,14 @@ class SceneController {
/// `stage.onHotReload.add((){
/// // your logic here
/// });`
- Signal _onHotReload;
+ Signal? _onHotReload;
Signal get onHotReload => _onHotReload ??= Signal();
- ScenePainter backScene, frontScene;
+ ScenePainter? backScene, frontScene;
/// Access the `ticker` (if any) created by this SceneController.
- GTicker get ticker {
+ GTicker? get ticker {
if (_ticker == null) {
throw 'You need to enable ticker usage with '
'SceneBuilderWidget( useTicker=true ) or '
@@ -38,19 +38,19 @@ class SceneController {
/// Access the keyboard manager instance associated with this
/// [SceneController].
- KeyboardManager get keyboard => _keyboard;
+ KeyboardManager? get keyboard => _keyboard;
/// Access the pointer manager instance associated with this
/// [SceneController].
- PointerManager get pointer => _pointer;
+ PointerManager? get pointer => _pointer;
- KeyboardManager _keyboard;
+ KeyboardManager? _keyboard;
- PointerManager _pointer;
+ PointerManager? _pointer;
- GTicker _ticker;
+ GTicker? _ticker;
- InputConverter $inputConverter;
+ InputConverter? $inputConverter;
SceneConfig get config => _config;
@@ -58,7 +58,7 @@ class SceneController {
/// gets widget's global coordinates.
/// useful to compute interactions with children Widgets that gets
- GRect Function() resolveWindowBounds;
+ GRect? Function()? resolveWindowBounds;
int id = -1;
@@ -76,9 +76,9 @@ class SceneController {
/// constructor.
SceneController({
- GSprite back,
- GSprite front,
- SceneConfig config,
+ GSprite? back,
+ GSprite? front,
+ SceneConfig? config,
}) {
assert(back != null || front != null);
if (back != null) {
@@ -99,9 +99,9 @@ class SceneController {
setup();
if (_hasTicker()) {
_ticker = GTicker();
- _ticker.onFrame.add(_onTick);
+ _ticker!.onFrame.add(_onTick);
if (_anySceneAutoUpdate()) {
- _ticker.resume();
+ _ticker!.resume();
}
}
_initInput();
@@ -129,7 +129,7 @@ class SceneController {
}
void dispose() {
- if (_config.isPersistent) {
+ if (_config.isPersistent ?? false) {
return;
}
_onHotReload?.removeAll();
@@ -140,25 +140,25 @@ class SceneController {
_isInited = false;
}
- CustomPainter buildBackPainter() => backScene?.buildPainter();
+ CustomPainter? buildBackPainter() => backScene?.buildPainter();
- CustomPainter buildFrontPainter() => frontScene?.buildPainter();
+ CustomPainter? buildFrontPainter() => frontScene?.buildPainter();
void _initInput() {
- if (_config.useKeyboard) {
+ if (_config.useKeyboard ?? false) {
_keyboard ??= KeyboardManager();
}
- if (_config.usePointer) {
+ if (_config.usePointer ?? false) {
_pointer ??= PointerManager();
}
- if (_config.useKeyboard || _config.usePointer) {
+ if (_config.useKeyboard ?? false || (_config.usePointer ?? false)) {
$inputConverter ??= InputConverter(_pointer, _keyboard);
}
}
void reassembleWidget() {
_onHotReload?.dispatch();
- if (_config.rebuildOnHotReload) {
+ if (_config.rebuildOnHotReload ?? false) {
GTween.hotReload();
dispose();
@@ -167,11 +167,11 @@ class SceneController {
}
}
- bool _sceneAutoUpdate(ScenePainter scene) =>
+ bool _sceneAutoUpdate(ScenePainter? scene) =>
scene?.autoUpdateAndRender ?? false;
bool _anySceneAutoUpdate() =>
_sceneAutoUpdate(backScene) || _sceneAutoUpdate(frontScene);
- bool _hasTicker() => _anySceneAutoUpdate() || _config.useTicker;
+ bool _hasTicker() => _anySceneAutoUpdate() || (_config.useTicker ?? false);
}
diff --git a/lib/src/core/scene_painter.dart b/lib/src/core/scene_painter.dart
index 1dbe1be..caa4412 100644
--- a/lib/src/core/scene_painter.dart
+++ b/lib/src/core/scene_painter.dart
@@ -7,7 +7,7 @@ import '../../graphx.dart';
class ScenePainter with EventDispatcherMixin {
/// Current painter being processed.
- static ScenePainter current;
+ static late ScenePainter current;
/// access to SceneController defined in the Widget side.
SceneController core;
@@ -26,16 +26,16 @@ class ScenePainter with EventDispatcherMixin {
/// Warning: Experimental state
bool useOwnCanvas = false;
bool _ownCanvasNeedsRepaint = false;
- Canvas $canvas;
+ Canvas? $canvas;
/// Size of the area available in `CustomPainter::paint()`
Size size = Size.zero;
/// Scene Layer's stage. The very first DisplayObject where the render
/// display list starts.
- Stage _stage;
+ Stage? _stage;
- Stage get stage => _stage;
+ Stage? get stage => _stage;
/// Defines when the Scene is ready and stage is accessible to GraphX root.
/// Runs on the first "render".
@@ -56,17 +56,17 @@ class ScenePainter with EventDispatcherMixin {
/// Ticker callback, to access the current frame delta timestamp
/// (in millisecond).
- EventSignal _onUpdate;
+ EventSignal? _onUpdate;
EventSignal get onUpdate => _onUpdate ??= EventSignal();
- ui.Picture _canvasPicture;
+ ui.Picture? _canvasPicture;
var _mouseMoveInputDetected = false;
var _lastMouseX = -1000000.0;
var _lastMouseY = -1000000.0;
var _lastMouseOut = false;
- MouseInputData _lastMouseInput;
+ MouseInputData? _lastMouseInput;
/// constructor.
ScenePainter(this.core, this.root) {
@@ -81,32 +81,33 @@ class ScenePainter with EventDispatcherMixin {
void _paint(Canvas canvas, Size size) {
if (this.size != size) {
this.size = size;
- _stage.$initFrameSize(size);
+ _stage!.$initFrameSize(size);
}
$canvas = canvas;
if (!_isReady) {
_isReady = true;
_initMouseInput();
- _stage.addChild(root);
+ _stage!.addChild(root);
_stage?.$onResized?.dispatch();
}
if (useOwnCanvas) {
_pictureFromCanvas();
} else {
- _stage.paint($canvas);
+ _stage!.paint($canvas);
}
}
void $setup() {
makeCurrent();
+
/// If needed, can be overridden later by the [root].
- autoUpdateAndRender = core.config.autoUpdateRender;
+ autoUpdateAndRender = core.config.autoUpdateRender ?? false;
}
void _createPicture() {
final _recorder = ui.PictureRecorder();
final _canvas = Canvas(_recorder);
- _stage.paint(_canvas);
+ _stage!.paint(_canvas);
_canvasPicture = _recorder.endRecording();
}
@@ -115,7 +116,7 @@ class ScenePainter with EventDispatcherMixin {
/// Manages the `update()` and can request to redraw the CustomPainter.
void tick(double time) {
makeCurrent();
- if (autoUpdateAndRender || time != null) {
+ if (autoUpdateAndRender) {
$currentFrameId++;
$runTime += time;
$update(time);
@@ -134,7 +135,7 @@ class ScenePainter with EventDispatcherMixin {
}
$currentFrameDeltaTime = deltaTime;
$accumulatedFrameDeltaTime += $currentFrameDeltaTime;
- _stage.$tick(deltaTime);
+ _stage!.$tick(deltaTime);
if (_hasPointer) {
_detectMouseMove();
}
@@ -153,9 +154,9 @@ class ScenePainter with EventDispatcherMixin {
input.stagePosition.setTo(_lastMouseX, _lastMouseY);
input.mouseOut = _lastMouseOut;
if (_lastMouseInput != null) {
- input.buttonDown = _lastMouseInput.buttonDown;
- input.rawEvent = _lastMouseInput.rawEvent;
- input.buttonsFlags = _lastMouseInput.buttonsFlags;
+ input.buttonDown = _lastMouseInput!.buttonDown;
+ input.rawEvent = _lastMouseInput!.rawEvent;
+ input.buttonsFlags = _lastMouseInput!.buttonsFlags;
}
_mouseInputHandler(input);
}
@@ -181,7 +182,7 @@ class ScenePainter with EventDispatcherMixin {
}
_lastMouseInput = input;
- stage.captureMouseInput(input);
+ stage!.captureMouseInput(input);
}
/// Requests a new frame.
@@ -200,7 +201,7 @@ class ScenePainter with EventDispatcherMixin {
}
void _initMouseInput() {
- core?.pointer?.onInput?.add(_onInputHandler);
+ core.pointer?.onInput.add(_onInputHandler);
}
void _onInputHandler(PointerEventData e) {
@@ -232,7 +233,7 @@ class ScenePainter with EventDispatcherMixin {
_createPicture();
}
if (_canvasPicture != null) {
- $canvas.drawPicture(_canvasPicture);
+ $canvas!.drawPicture(_canvasPicture!);
}
}
@@ -241,13 +242,13 @@ class ScenePainter with EventDispatcherMixin {
_isReady = false;
size = Size.zero;
_stage?.dispose();
- core?.pointer?.onInput?.remove(_onInputHandler);
+ core.pointer?.onInput.remove(_onInputHandler);
_onUpdate?.removeAll();
_onUpdate = null;
super.dispose();
}
- bool get _hasPointer => core?.pointer?.onInput != null;
+ bool get _hasPointer => core.pointer?.onInput != null;
bool shouldRepaint() => needsRepaint;
}
diff --git a/lib/src/debug/display_debugger.dart b/lib/src/debug/display_debugger.dart
index 9b01fb9..b22ee0d 100644
--- a/lib/src/debug/display_debugger.dart
+++ b/lib/src/debug/display_debugger.dart
@@ -30,7 +30,7 @@ class DisplayBoundsDebugger {
..strokeWidth = 1;
final GDisplayObjectContainer _root;
- ui.Canvas canvas;
+ ui.Canvas? canvas;
static final GRect _sHelpRect = GRect();
DisplayBoundsDebugger(GDisplayObjectContainer root) : _root = root;
@@ -59,8 +59,8 @@ class DisplayBoundsDebugger {
final linePaint = _paint.clone();
linePaint.color = linePaint.color.withOpacity(.3);
final rect = _sHelpRect.toNative();
- canvas.drawLine(rect.topLeft, rect.bottomRight, linePaint);
- canvas.drawLine(rect.topRight, rect.bottomLeft, linePaint);
- canvas.drawRect(rect, _paint);
+ canvas!.drawLine(rect.topLeft, rect.bottomRight, linePaint);
+ canvas!.drawLine(rect.topRight, rect.bottomLeft, linePaint);
+ canvas!.drawRect(rect, _paint);
}
}
diff --git a/lib/src/display/bitmap.dart b/lib/src/display/bitmap.dart
index ddad535..4f2a6bf 100644
--- a/lib/src/display/bitmap.dart
+++ b/lib/src/display/bitmap.dart
@@ -11,12 +11,13 @@ class GBitmap extends GDisplayObject {
return '$runtimeType (Bitmap2)$msg';
}
- GTexture _texture;
- GTexture get texture => _texture;
+ GTexture? _texture;
+ GTexture? get texture => _texture;
double $originalPivotX = 0, $originalPivotY = 0;
+
/// TODO: improve this process, make bounds work properly.
- bool _hasScale9Grid;
- double _buffScaleX, _buffScaleY;
+ late bool _hasScale9Grid;
+ double _buffScaleX = 0.0, _buffScaleY = 0.0;
final GRect _cachedBounds = GRect();
final _paint = ui.Paint()..filterQuality = ui.FilterQuality.medium;
ui.Paint get nativePaint => _paint;
@@ -33,28 +34,28 @@ class GBitmap extends GDisplayObject {
super.pivotY = value;
}
- set texture(GTexture value) {
+ set texture(GTexture? value) {
if (_texture == value) return;
_texture = value;
if (_texture != null) {
- pivotX = -_texture.pivotX + $originalPivotX;
- pivotY = -_texture.pivotY + $originalPivotY;
+ pivotX = -_texture!.pivotX! + $originalPivotX;
+ pivotY = -_texture!.pivotY! + $originalPivotY;
}
requiresRedraw();
}
- GBitmap([GTexture texture]) {
+ GBitmap([GTexture? texture]) {
this.texture = texture;
}
@override
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect getBounds(GDisplayObject? targetSpace, [GRect? out]) {
out ??= GRect();
final matrix = _sHelperMatrix;
matrix.identity();
getTransformationMatrix(targetSpace, matrix);
if (texture != null) {
- var rect = texture.getBounds();
+ var rect = texture!.getBounds()!;
out = MatrixUtils.getTransformedBoundsRect(
matrix,
rect,
@@ -74,15 +75,15 @@ class GBitmap extends GDisplayObject {
}
@override
- set colorize(ui.Color value) {
+ set colorize(ui.Color? value) {
if ($colorize == value) return;
super.colorize = value;
_paint.colorFilter =
- $hasColorize ? PainterUtils.getColorize($colorize) : null;
+ $hasColorize ? PainterUtils.getColorize($colorize!) : null;
}
@override
- set filters(List value) {
+ set filters(List? value) {
if ($filters == value) return;
$filters = value;
if ($filters == null) {
@@ -92,9 +93,9 @@ class GBitmap extends GDisplayObject {
}
@override
- void paint(ui.Canvas canvas) {
+ void paint(ui.Canvas? canvas) {
if (texture == null) return;
- _hasScale9Grid = texture.scale9Grid != null;
+ _hasScale9Grid = texture!.scale9Grid != null;
if (_hasScale9Grid) {
_adjustScaleGrid();
}
@@ -102,9 +103,9 @@ class GBitmap extends GDisplayObject {
}
@override
- void $applyPaint(ui.Canvas canvas) {
+ void $applyPaint(ui.Canvas? canvas) {
if (hasFilters) {
- for( var filter in filters ){
+ for (var filter in filters!) {
filter.update();
filter.resolvePaint(_paint);
}
@@ -119,9 +120,9 @@ class GBitmap extends GDisplayObject {
super.pivotX = $originalPivotX * _buffScaleX;
super.pivotY = $originalPivotY * _buffScaleY;
_cachedBounds.x = _cachedBounds.y = 0;
- _cachedBounds.width = texture.width * _buffScaleX;
- _cachedBounds.height = texture.height * _buffScaleY;
- texture.scale9GridDest = _cachedBounds;
+ _cachedBounds.width = texture!.width! * _buffScaleX;
+ _cachedBounds.height = texture!.height! * _buffScaleY;
+ texture!.scale9GridDest = _cachedBounds;
setScale(1, 1);
}
}
diff --git a/lib/src/display/display_object.dart b/lib/src/display/display_object.dart
index f0bfbaf..0831b3e 100644
--- a/lib/src/display/display_object.dart
+++ b/lib/src/display/display_object.dart
@@ -9,10 +9,10 @@ abstract class GDisplayObject
RenderSignalMixin,
MouseSignalsMixin,
DisplayMasking {
- GDisplayObjectContainer $parent;
- static GDisplayObject $currentDrag;
- static GRect $currentDragBounds;
- GPoint _dragCenterOffset;
+ GDisplayObjectContainer? $parent;
+ static GDisplayObject? $currentDrag;
+ static GRect? $currentDragBounds;
+ late GPoint _dragCenterOffset;
/// Lets the user drag the specified sprite.
/// The sprite remains draggable until explicitly stopped through a call to
@@ -23,7 +23,7 @@ abstract class GDisplayObject
/// the user first clicked the sprite (false).
/// [bounds] Value relative to the coordinates of the Sprite's parent that
/// specify a constraint rectangle for the Sprite.
- void startDrag([bool lockCenter = false, GRect bounds]) {
+ void startDrag([bool lockCenter = false, GRect? bounds]) {
if (!inStage || !$hasTouchableArea) {
throw 'to drag an object, it has to be visible and enabled in the stage.';
}
@@ -33,9 +33,9 @@ abstract class GDisplayObject
$currentDragBounds = bounds;
_dragCenterOffset = GPoint();
if (lockCenter) {
- _dragCenterOffset.setTo(x - parent.mouseX, y - parent.mouseY);
+ _dragCenterOffset.setTo(x - parent!.mouseX, y - parent!.mouseY);
}
- stage.onMouseMove.add(_handleDrag);
+ stage!.onMouseMove.add(_handleDrag);
}
// DisplayObject get dropTarget {
@@ -50,14 +50,14 @@ abstract class GDisplayObject
void _handleDrag(MouseInputData input) {
if (this != $currentDrag) {
- stage?.onMouseMove?.remove(_handleDrag);
+ stage?.onMouseMove.remove(_handleDrag);
}
if ($currentDrag == null) {
$currentDragBounds = null;
return;
}
- var tx = $currentDrag.parent.mouseX + _dragCenterOffset.x;
- var ty = $currentDrag.parent.mouseY + _dragCenterOffset.y;
+ var tx = $currentDrag!.parent!.mouseX + _dragCenterOffset.x;
+ var ty = $currentDrag!.parent!.mouseY + _dragCenterOffset.y;
final rect = $currentDragBounds;
if (rect != null) {
tx = tx.clamp(rect.left, rect.right);
@@ -68,7 +68,7 @@ abstract class GDisplayObject
void stopDrag() {
if (this == $currentDrag) {
- stage.onMouseMove.remove(_handleDrag);
+ stage!.onMouseMove.remove(_handleDrag);
$currentDrag = null;
}
}
@@ -76,26 +76,26 @@ abstract class GDisplayObject
bool $debugBounds = false;
bool mouseUseShape = false;
- List $filters;
+ List? $filters;
- List get filters => $filters;
+ List? get filters => $filters;
- set filters(List value) => $filters = value;
+ set filters(List? value) => $filters = value;
- GDisplayObject $mouseDownObj;
- GDisplayObject $mouseOverObj;
+ GDisplayObject? $mouseDownObj;
+ GDisplayObject? $mouseOverObj;
double $lastClickTime = -1;
bool useCursor = false;
- ui.Color $colorize = kColorTransparent;
+ ui.Color? $colorize = kColorTransparent;
- bool get $hasColorize => $colorize != null && $colorize.alpha > 0;
+ bool get $hasColorize => $colorize != null && $colorize!.alpha > 0;
- ui.Color get colorize => $colorize;
+ ui.Color? get colorize => $colorize;
- set colorize(ui.Color value) {
+ set colorize(ui.Color? value) {
if ($colorize == value) return;
$colorize = value;
requiresRedraw();
@@ -127,7 +127,7 @@ abstract class GDisplayObject
}
void $dispatchMouseCallback(
- MouseInputType type,
+ MouseInputType? type,
GDisplayObject object,
MouseInputData input,
) {
@@ -198,7 +198,7 @@ abstract class GDisplayObject
double get mouseX {
if (inStage) {
- return globalToLocal(_sHelperPoint.setTo(stage.pointer.mouseX, 0)).x;
+ return globalToLocal(_sHelperPoint.setTo(stage!.pointer!.mouseX, 0)).x;
} else {
throw 'To get mouseX object needs to be a descendant of Stage.';
}
@@ -206,7 +206,7 @@ abstract class GDisplayObject
double get mouseY {
if (inStage) {
- return globalToLocal(_sHelperPoint.setTo(0, stage.pointer.mouseY)).y;
+ return globalToLocal(_sHelperPoint.setTo(0, stage!.pointer!.mouseY)).y;
} else {
throw 'To get mouseY object needs to be a descendant of Stage.';
}
@@ -217,14 +217,14 @@ abstract class GDisplayObject
throw 'To get mousePosition, the object needs to be in the Stage.';
}
return globalToLocal(_sHelperPoint.setTo(
- stage.pointer.mouseX,
- stage.pointer.mouseY,
+ stage!.pointer!.mouseX,
+ stage!.pointer!.mouseY,
));
}
/// You can store any user defined data in this property for easy access.
- Object userData;
- String name;
+ Object? userData;
+ String? name;
double _x = 0, _y = 0, _scaleX = 1, _scaleY = 1, _rotation = 0;
double _pivotX = 0, _pivotY = 0;
@@ -270,7 +270,7 @@ abstract class GDisplayObject
/// ```
/// A display object with no content (such as an empty sprite) has a width
/// of 0, even if you try to set width to a different value.
- double get width => getBounds($parent, _sHelperRect).width;
+ double get width => getBounds($parent, _sHelperRect)!.width;
/// Indicates the height of the display object, in dp.
/// The `height` is calculated based on the bounds of the content of the
@@ -287,11 +287,11 @@ abstract class GDisplayObject
/// ```
/// A display object with no content (such as an empty sprite) has a height
/// of 0, even if you try to set height to a different value.
- double get height => getBounds($parent, _sHelperRect).height;
+ double get height => getBounds($parent, _sHelperRect)!.height;
- set width(double value) {
+ set width(double? value) {
if (value == null) throw 'width can not be null';
- double actualW;
+ double? actualW;
var zeroScale = _scaleX < 1e-8 && _scaleX > -1e-8;
if (zeroScale) {
scaleX = 1.0;
@@ -299,12 +299,12 @@ abstract class GDisplayObject
} else {
actualW = (width / _scaleX).abs();
}
- if (actualW != null) scaleX = value / actualW;
+ scaleX = value / actualW;
}
- set height(double value) {
+ set height(double? value) {
if (value == null) throw 'height can not be null';
- double actualH;
+ double? actualH;
var zeroScale = _scaleY < 1e-8 && _scaleY > -1e-8;
if (zeroScale) {
scaleY = 1.0;
@@ -312,31 +312,31 @@ abstract class GDisplayObject
} else {
actualH = (height / _scaleY).abs();
}
- if (actualH != null) scaleY = value / actualH;
+ scaleY = value / actualH;
}
- set x(double value) {
+ set x(double? value) {
if (value == null) throw 'x can not be null';
if (_x == value) return;
_x = value;
$setTransformationChanged();
}
- set y(double value) {
+ set y(double? value) {
if (value == null) throw 'y can not be null';
if (_y == value) return;
_y = value;
$setTransformationChanged();
}
- set scaleX(double value) {
+ set scaleX(double? value) {
if (value == null) throw 'scaleX can not be null';
if (_scaleX == value) return;
_scaleX = value;
$setTransformationChanged();
}
- set scaleY(double value) {
+ set scaleY(double? value) {
if (value == null) throw 'scaleY can not be null';
if (_scaleY == value) return;
_scaleY = value;
@@ -345,31 +345,29 @@ abstract class GDisplayObject
set pivotX(double value) {
if (_pivotX == value) return;
- _pivotX = value ?? 0.0;
+ _pivotX = value;
$setTransformationChanged();
}
set pivotY(double value) {
if (_pivotY == value) return;
- _pivotY = value ?? 0.0;
+ _pivotY = value;
$setTransformationChanged();
}
set skewX(double value) {
- if (value == null) throw 'skewX can not be null';
if (_skewX == value) return;
_skewX = value;
$setTransformationChanged();
}
set skewY(double value) {
- if (value == null) throw 'skewY can not be null';
if (_skewY == value) return;
_skewY = value;
$setTransformationChanged();
}
- set rotation(double value) {
+ set rotation(double? value) {
if (value == null) throw 'rotation can not be null';
if (_rotation == value) return;
_rotation = value;
@@ -377,11 +375,8 @@ abstract class GDisplayObject
}
set rotationX(double value) {
- if (value == null) {
- throw 'rotationX can not be null';
- }
if (_rotationX == value) return;
- _rotationX = value ?? 0.0;
+ _rotationX = value;
if (!_isWarned3d) _warn3d();
$setTransformationChanged();
}
@@ -394,21 +389,15 @@ abstract class GDisplayObject
}
set rotationY(double value) {
- if (value == null) {
- throw 'rotationY can not be null';
- }
if (_rotationY == value) return;
- _rotationY = value ?? 0.0;
+ _rotationY = value;
if (!_isWarned3d) _warn3d();
$setTransformationChanged();
}
set z(double value) {
- if (value == null) {
- throw 'z can not be null';
- }
if (_z == value) return;
- _z = value ?? 0.0;
+ _z = value;
if (!_isWarned3d) _warn3d();
$setTransformationChanged();
}
@@ -418,11 +407,8 @@ abstract class GDisplayObject
double get alpha => $alpha;
set alpha(double value) {
- if (value == null) {
- throw 'alpha can not be null';
- }
if ($alpha != value) {
- value ??= 1;
+ // value ??= 1;
$alpha = value.clamp(0.0, 1.0);
requiresRedraw();
}
@@ -433,8 +419,8 @@ abstract class GDisplayObject
bool $matrixDirty = true;
bool mouseEnabled = true;
- GDisplayObject $maskee;
- GShape $mask;
+ GDisplayObject? $maskee;
+ GShape? $mask;
/// optimization.
bool $hasTouchableArea = true;
@@ -442,14 +428,14 @@ abstract class GDisplayObject
bool get isMask => $maskee != null;
- GShape get mask => $mask;
+ GShape? get mask => $mask;
/// can be set on the Shape mask, or the maskee DisplayObject.
bool maskInverted = false;
- set mask(GShape value) {
+ set mask(GShape? value) {
if ($mask != value) {
- if ($mask != null) $mask.$maskee = null;
+ if ($mask != null) $mask!.$maskee = null;
value?.$maskee = this;
value?.$hasVisibleArea = false;
$mask = value;
@@ -512,7 +498,7 @@ abstract class GDisplayObject
}
void alignPivot([painting.Alignment alignment = painting.Alignment.center]) {
- var bounds = getBounds(this, _sHelperRect);
+ var bounds = getBounds(this, _sHelperRect)!;
if (bounds.isEmpty) return;
var ax = 0.5 + alignment.x / 2;
var ay = 0.5 + alignment.y / 2;
@@ -522,24 +508,24 @@ abstract class GDisplayObject
/// local bounds
/// todo: should be cached.
- GRect get bounds => getBounds(this);
+ GRect? get bounds => getBounds(this);
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect? getBounds(GDisplayObject? targetSpace, [GRect? out]) {
throw 'getBounds() is abstract in DisplayObject';
}
- GPoint globalToLocal(GPoint globalPoint, [GPoint out]) {
+ GPoint globalToLocal(GPoint globalPoint, [GPoint? out]) {
getTransformationMatrix(base, _sHelperMatrixAlt);
_sHelperMatrixAlt.invert();
return _sHelperMatrixAlt.transformPoint(globalPoint, out);
}
- GPoint localToGlobal(GPoint localPoint, [GPoint out]) {
+ GPoint localToGlobal(GPoint localPoint, [GPoint? out]) {
getTransformationMatrix(base, _sHelperMatrixAlt);
return _sHelperMatrixAlt.transformPoint(localPoint, out);
}
- GMatrix _transformationMatrix;
+ GMatrix? _transformationMatrix;
GMatrix get transformationMatrix {
if (_transformationChanged || _transformationMatrix == null) {
@@ -566,10 +552,10 @@ abstract class GDisplayObject
skewX,
skewY,
rotation,
- _transformationMatrix,
+ _transformationMatrix!,
);
}
- return _transformationMatrix;
+ return _transformationMatrix!;
}
set transformationMatrix(GMatrix matrix) {
@@ -577,7 +563,7 @@ abstract class GDisplayObject
requiresRedraw();
_transformationChanged = false;
_transformationMatrix ??= GMatrix();
- _transformationMatrix.copyFrom(matrix);
+ _transformationMatrix!.copyFrom(matrix);
_pivotX = _pivotY = 0;
_x = matrix.tx;
_y = matrix.ty;
@@ -598,8 +584,8 @@ abstract class GDisplayObject
}
void $updateTransformationMatrices(
- double x,
- double y,
+ double? x,
+ double? y,
double pivotX,
double pivotY,
double scaleX,
@@ -618,8 +604,8 @@ abstract class GDisplayObject
0,
0,
scaleY,
- x - pivotX * scaleX,
- y - pivotY * scaleY,
+ x! - pivotX * scaleX,
+ y! - pivotY * scaleY,
);
} else {
final cos = Math.cos(rotation);
@@ -628,8 +614,8 @@ abstract class GDisplayObject
final b = scaleX * sin;
final c = scaleY * -sin;
final d = scaleY * cos;
- final tx = x - pivotX * a - pivotY * c;
- final ty = y - pivotX * b - pivotY * d;
+ final tx = x! - pivotX * a - pivotY * c;
+ final ty = y! - pivotX * b - pivotY * d;
out.setTo(a, b, c, d, tx, ty);
}
} else {
@@ -637,7 +623,7 @@ abstract class GDisplayObject
out.scale(scaleX, scaleY);
out.skew(skewX, skewY); // MatrixUtils.skew(out, skewX, skewY);
out.rotate(rotation);
- out.translate(x, y);
+ out.translate(x!, y!);
if (pivotX != 0 || pivotY != 0) {
out.tx = x - out.a * pivotX - out.c * pivotY;
out.ty = y - out.b * pivotX - out.d * pivotY;
@@ -645,8 +631,8 @@ abstract class GDisplayObject
}
}
- GMatrix getTransformationMatrix(GDisplayObject targetSpace, [GMatrix out]) {
- GDisplayObject commonParent, currentObj;
+ GMatrix getTransformationMatrix(GDisplayObject? targetSpace, [GMatrix? out]) {
+ GDisplayObject? commonParent, currentObj;
out?.identity();
out ??= GMatrix();
if (targetSpace == this) {
@@ -659,7 +645,7 @@ abstract class GDisplayObject
if (targetSpace == null || targetSpace == base) {
currentObj = this;
while (currentObj != targetSpace) {
- out.concat(currentObj.transformationMatrix);
+ out.concat(currentObj!.transformationMatrix);
currentObj = currentObj.$parent;
}
return out;
@@ -677,7 +663,7 @@ abstract class GDisplayObject
/// 2 - move up from this to common parent.````
currentObj = this;
while (currentObj != commonParent) {
- out.concat(currentObj.transformationMatrix);
+ out.concat(currentObj!.transformationMatrix);
currentObj = currentObj.$parent;
}
@@ -687,7 +673,7 @@ abstract class GDisplayObject
_sHelperMatrix.identity();
currentObj = targetSpace;
while (currentObj != commonParent) {
- _sHelperMatrix.concat(currentObj.transformationMatrix);
+ _sHelperMatrix.concat(currentObj!.transformationMatrix);
currentObj = currentObj.$parent;
}
@@ -699,7 +685,7 @@ abstract class GDisplayObject
static GDisplayObject _findCommonParent(
GDisplayObject obj1, GDisplayObject obj2) {
- var current = obj1;
+ GDisplayObject? current = obj1;
/// TODO: use faster Hash access.
while (current != null) {
@@ -720,20 +706,20 @@ abstract class GDisplayObject
return true;
}
if (maskRect != null) {
- final isHit = maskRect.containsPoint(localPoint);
+ final isHit = maskRect!.containsPoint(localPoint);
return maskRectInverted ? !isHit : isHit;
}
- if ($mask.inStage) {
+ if ($mask!.inStage) {
getTransformationMatrix($mask, _sHelperMatrixAlt);
} else {
- _sHelperMatrixAlt.copyFrom($mask.transformationMatrix);
+ _sHelperMatrixAlt.copyFrom($mask!.transformationMatrix);
_sHelperMatrixAlt.invert();
}
var helperPoint = localPoint == _sHelperPoint ? GPoint() : _sHelperPoint;
_sHelperMatrixAlt.transformPoint(localPoint, helperPoint);
- final isHit = mask.hitTest(helperPoint) != null;
+ final isHit = mask!.hitTest(helperPoint) != null;
// return maskInverted ? !isHit : isHit;
return maskInverted ? !isHit : isHit;
}
@@ -743,38 +729,38 @@ abstract class GDisplayObject
}
/// `useShape` is meant to be used by `Shape.graphics`.
- GDisplayObject hitTest(GPoint localPoint, [bool useShape = false]) {
+ GDisplayObject? hitTest(GPoint localPoint, [bool useShape = false]) {
if (!$hasTouchableArea || !mouseEnabled) {
return null;
}
if (($mask != null || maskRect != null) && !hitTestMask(localPoint)) {
return null;
}
- if (getBounds(this, _sHelperRect).containsPoint(localPoint)) {
+ if (getBounds(this, _sHelperRect)!.containsPoint(localPoint)) {
return this;
}
return null;
}
- GDisplayObjectContainer get parent => $parent;
+ GDisplayObjectContainer? get parent => $parent;
GDisplayObject get base {
var current = this;
while (current.$parent != null) {
- current = current.$parent;
+ current = current.$parent!;
}
return current;
}
bool get inStage => base is Stage;
- Stage get stage => base is Stage ? base : null;
+ Stage? get stage => base is Stage ? base as Stage? : null;
- GDisplayObject get root {
+ GDisplayObject? get root {
var current = this;
while (current.$parent != null) {
if (current.$parent is Stage) return current;
- current = current.$parent;
+ current = current.$parent!;
}
return null;
}
@@ -800,7 +786,7 @@ abstract class GDisplayObject
void update(double delta) {}
bool get hasFilters => filters?.isNotEmpty ?? false;
- GRect $debugLastLayerBounds;
+ GRect? $debugLastLayerBounds;
/// quick and dirty way to toggle saveLayer() feature for common
/// display objects as well.
@@ -808,14 +794,14 @@ abstract class GDisplayObject
/// Paint() so no need to use an expensive saveLayer().
bool allowSaveLayer = false;
- GRect getFilterBounds([GRect layerBounds, ui.Paint alphaPaint]) {
+ GRect? getFilterBounds([GRect? layerBounds, ui.Paint? alphaPaint]) {
layerBounds ??= getBounds($parent);
- if ($filters == null || $filters.isEmpty) {
+ if ($filters == null || $filters!.isEmpty) {
return layerBounds;
}
- layerBounds = layerBounds.clone();
- GRect resultBounds;
- for (var filter in $filters) {
+ layerBounds = layerBounds!.clone();
+ GRect? resultBounds;
+ for (var filter in $filters!) {
resultBounds ??= layerBounds.clone();
if (alphaPaint != null) {
filter.update();
@@ -837,7 +823,7 @@ abstract class GDisplayObject
/// Do not override this method as it applies the basic
/// transformations. Override $applyPaint() if you wanna use
/// `Canvas` directly.
- void paint(ui.Canvas canvas) {
+ void paint(ui.Canvas? canvas) {
if (!$hasVisibleArea || !visible) {
return;
}
@@ -868,13 +854,13 @@ abstract class GDisplayObject
DisplayBoundsDebugger.debugBoundsMode == DebugBoundsMode.internal &&
($debugBounds || DisplayBoundsDebugger.debugAll);
- GRect _cacheLocalBoundsRect;
+ GRect? _cacheLocalBoundsRect;
if (showDebugBounds || _saveLayer) {
// _cacheLocalBoundsRect = bounds.toNative();
_cacheLocalBoundsRect = bounds;
}
- List _composerFilters;
+ List? _composerFilters;
var filterHidesObject = false;
if (_saveLayer) {
// TODO: static painter seems to have some issues, try local var later.
@@ -890,18 +876,18 @@ abstract class GDisplayObject
alphaPaint.maskFilter = null;
if ($hasColorize) {
alphaPaint.colorFilter = ui.ColorFilter.mode(
- $colorize,
+ $colorize!,
ui.BlendMode.srcATop,
);
}
- ui.Rect nativeLayerBounds;
+ ui.Rect? nativeLayerBounds;
var layerBounds = getBounds($parent);
if ($hasFilters) {
/// TODO: Find a common implementation for filter bounds.
// layerBounds = getFilterBounds(layerBounds, alphaPaint);
- layerBounds = layerBounds.clone();
- GRect resultBounds;
- for (var filter in $filters) {
+ layerBounds = layerBounds!.clone();
+ GRect? resultBounds;
+ for (var filter in $filters!) {
resultBounds ??= layerBounds.clone();
filter.update();
filter.expandBounds(layerBounds, resultBounds);
@@ -917,18 +903,18 @@ abstract class GDisplayObject
$debugLastLayerBounds = layerBounds;
// canvas.saveLayer(layerBounds.toNative(), alphaPaint);
if ($useSaveLayerBounds) {
- nativeLayerBounds = layerBounds.toNative();
+ nativeLayerBounds = layerBounds!.toNative();
}
- canvas.saveLayer(nativeLayerBounds, alphaPaint);
+ canvas!.saveLayer(nativeLayerBounds, alphaPaint);
}
if (needSave) {
// onPreTransform.dispatch();
- canvas.save();
- var m = transformationMatrix.toNative();
+ canvas!.save();
+ var m = transformationMatrix.toNative()!;
canvas.transform(m.storage);
if (_is3D) {
/// TODO: experimental, just transforms
- m = GMatrix().toNative();
+ m = GMatrix().toNative()!;
m.setEntry(3, 2, 0.004);
m.rotateX(_rotationX);
m.rotateY(_rotationY);
@@ -940,11 +926,11 @@ abstract class GDisplayObject
}
if (hasMask) {
- canvas.save();
+ canvas!.save();
if (maskRect != null) {
$applyMaskRect(canvas);
} else {
- mask.$applyPaint(canvas);
+ mask!.$applyPaint(canvas);
}
}
@@ -964,22 +950,22 @@ abstract class GDisplayObject
$onPostPaint?.dispatch(canvas);
if (hasMask) {
- canvas.restore();
+ canvas!.restore();
}
if (showDebugBounds) {
final _paint = $debugBoundsPaint ?? _debugPaint;
final linePaint = _paint.clone();
linePaint.color = linePaint.color.withOpacity(.3);
- final rect = _cacheLocalBoundsRect.toNative();
- canvas.drawLine(rect.topLeft, rect.bottomRight, linePaint);
+ final rect = _cacheLocalBoundsRect!.toNative();
+ canvas!.drawLine(rect.topLeft, rect.bottomRight, linePaint);
canvas.drawLine(rect.topRight, rect.bottomLeft, linePaint);
canvas.drawRect(rect, _paint);
}
if (needSave) {
- canvas.restore();
+ canvas!.restore();
}
if (_saveLayer) {
- canvas.restore();
+ canvas!.restore();
}
}
@@ -988,11 +974,11 @@ abstract class GDisplayObject
..color = kColorMagenta
..strokeWidth = 1;
- ui.Paint $debugBoundsPaint = _debugPaint.clone();
+ ui.Paint? $debugBoundsPaint = _debugPaint.clone();
/// override this method for custom drawing using Flutter's API.
/// Access `$canvas` from here.
- void $applyPaint(ui.Canvas canvas) {}
+ void $applyPaint(ui.Canvas? canvas) {}
@mustCallSuper
void dispose() {
@@ -1010,7 +996,7 @@ abstract class GDisplayObject
}
/// internal
- void $setParent(GDisplayObjectContainer value) {
+ void $setParent(GDisplayObjectContainer? value) {
var ancestor = value;
while (ancestor != this && ancestor != null) {
ancestor = ancestor.$parent;
@@ -1039,7 +1025,7 @@ abstract class GDisplayObject
$setTransformationChanged();
}
- void setScale(double scaleX, [double scaleY]) {
+ void setScale(double scaleX, [double? scaleY]) {
_scaleX = scaleX;
_scaleY = scaleY ?? scaleX;
$setTransformationChanged();
@@ -1051,8 +1037,8 @@ abstract class GDisplayObject
/// transformations (x, y, scale, etc) if you intend to use in it's "original"
/// form.
ui.Picture createPicture(
- [void Function(ui.Canvas) prePaintCallback,
- void Function(ui.Canvas) postPaintCallback]) {
+ [void Function(ui.Canvas)? prePaintCallback,
+ void Function(ui.Canvas)? postPaintCallback]) {
final r = ui.PictureRecorder();
final c = ui.Canvas(r);
prePaintCallback?.call(c);
@@ -1064,22 +1050,22 @@ abstract class GDisplayObject
Future createImageTexture([
bool adjustOffset = true,
double resolution = 1,
- GRect rect,
+ GRect? rect,
]) async {
final img = await createImage(adjustOffset, resolution, rect);
var tx = GTexture.fromImage(img, resolution);
- tx.pivotX = bounds.x;
- tx.pivotY = bounds.y;
+ tx.pivotX = bounds!.x;
+ tx.pivotY = bounds!.y;
return tx;
}
Future createImage([
bool adjustOffset = true,
double resolution = 1,
- GRect rect,
+ GRect? rect,
]) async {
rect ??= getFilterBounds(); //getBounds($parent);
- rect = rect.clone();
+ rect = rect!.clone();
if (resolution != 1) {
rect *= resolution;
}
@@ -1088,7 +1074,7 @@ abstract class GDisplayObject
ui.Picture picture;
if (needsAdjust) {
picture = createPicture((canvas) {
- if (adjustOffset) canvas.translate(-rect.left, -rect.top);
+ if (adjustOffset) canvas.translate(-rect!.left, -rect.top);
if (resolution != 1) canvas.scale(resolution);
}, (canvas) {
if (adjustOffset) canvas.restore();
diff --git a/lib/src/display/display_object_container.dart b/lib/src/display/display_object_container.dart
index 8920d52..8a3a0bf 100644
--- a/lib/src/display/display_object_container.dart
+++ b/lib/src/display/display_object_container.dart
@@ -41,7 +41,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
}
@override
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect? getBounds(GDisplayObject? targetSpace, [GRect? out]) {
out ??= GRect();
final len = children.length;
if (len == 0) {
@@ -76,7 +76,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
return result;
}
final numChild = children.length;
- GDisplayObject target;
+ GDisplayObject? target;
for (var i = numChild - 1; i >= 0; --i) {
var child = children[i];
if (child.isMask) continue;
@@ -99,7 +99,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
}
@override
- GDisplayObject hitTest(GPoint localPoint, [bool useShape = false]) {
+ GDisplayObject? hitTest(GPoint localPoint, [bool useShape = false]) {
if (!$hasTouchableArea || !mouseEnabled || !hitTestMask(localPoint)) {
return null;
}
@@ -133,7 +133,6 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
}
GDisplayObject addChildAt(GDisplayObject child, int index) {
- if (child == null) throw "::child can't be null";
if (index < 0 || index > children.length) {
throw RangeError('Invalid child index');
}
@@ -209,7 +208,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
throw RangeError('Invalid child index');
}
- GDisplayObject getChildByName(String name) {
+ GDisplayObject? getChildByName(String name) {
for (final child in children) {
if (child.name == name) return child;
}
@@ -237,7 +236,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
bool get hasChildren => children.isNotEmpty;
- bool contains(GDisplayObject child, [bool recursive = true]) {
+ bool contains(GDisplayObject? child, [bool recursive = true]) {
if (!recursive) return children.contains(child);
while (child != null) {
if (child == this) return true;
@@ -267,8 +266,8 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
throw 'Invalid child index';
}
- GDisplayObject removeChild(GDisplayObject child, [bool dispose = false]) {
- if (child == null || child?.$parent != this) return null;
+ GDisplayObject? removeChild(GDisplayObject child, [bool dispose = false]) {
+ if (child.$parent != this) return null;
final index = getChildIndex(child);
if (index > -1) return removeChildAt(index, dispose);
throw 'Invalid child index';
@@ -283,7 +282,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
}
@override
- void $applyPaint(ui.Canvas canvas) {
+ void $applyPaint(ui.Canvas? canvas) {
if (!$hasVisibleArea) return;
for (var child in children) {
if (child.$hasVisibleArea) {
@@ -304,7 +303,7 @@ abstract class GDisplayObjectContainer extends GDisplayObject {
@mustCallSuper
void dispose() {
for (final child in children) {
- child?.dispose();
+ child.dispose();
}
children.clear();
super.dispose();
diff --git a/lib/src/display/shape.dart b/lib/src/display/shape.dart
index 838f553..dbe35c2 100644
--- a/lib/src/display/shape.dart
+++ b/lib/src/display/shape.dart
@@ -2,7 +2,7 @@ import 'dart:ui' as ui;
import '../../graphx.dart';
class GShape extends GDisplayObject {
- Graphics _graphics;
+ Graphics? _graphics;
static final _sHelperMatrix = GMatrix();
@override
@@ -14,7 +14,7 @@ class GShape extends GDisplayObject {
Graphics get graphics => _graphics ??= Graphics();
@override
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect? getBounds(GDisplayObject? targetSpace, [GRect? out]) {
final matrix = _sHelperMatrix;
matrix.identity();
getTransformationMatrix(targetSpace, matrix);
@@ -33,24 +33,24 @@ class GShape extends GDisplayObject {
// });
/// single bounds, all paths as 1 rect.
return MatrixUtils.getTransformedBoundsRect(
- matrix, _graphics.getBounds(out), out);
+ matrix, _graphics!.getBounds(out), out);
} else {
final pos = GDisplayObjectContainer.$sBoundsPoint;
matrix.transformCoords(0, 0, pos);
- out.setTo(pos.x, pos.y, 0, 0);
+ out!.setTo(pos.x, pos.y, 0, 0);
}
return out;
}
@override
- GDisplayObject hitTest(GPoint localPoint, [bool useShape = false]) {
+ GDisplayObject? hitTest(GPoint localPoint, [bool useShape = false]) {
if (!$hasTouchableArea || !mouseEnabled) {
return null;
}
return (_graphics?.hitTest(localPoint, useShape) ?? false) ? this : null;
}
- static ui.Path _inverseHugePath;
+ static ui.Path? _inverseHugePath;
static void _initInversePath() {
if (_inverseHugePath != null) {
return;
@@ -58,22 +58,22 @@ class GShape extends GDisplayObject {
_inverseHugePath = ui.Path();
final w = 100000.0;
var r = Pool.getRect(-w / 2, -w / 2, w, w);
- _inverseHugePath.addRect(r.toNative());
- _inverseHugePath.close();
+ _inverseHugePath!.addRect(r.toNative());
+ _inverseHugePath!.close();
}
@override
- void $applyPaint(ui.Canvas canvas) {
+ void $applyPaint(ui.Canvas? canvas) {
if (isMask && _graphics != null) {
GMatrix matrix;
- var paths = _graphics.getPaths();
- if (inStage && $maskee.inStage) {
+ var paths = _graphics!.getPaths();
+ if (inStage && $maskee!.inStage) {
matrix = getTransformationMatrix($maskee);
} else {
matrix = transformationMatrix;
}
- var clipPath = paths.transform(matrix.toNative().storage);
- final inverted = maskInverted || $maskee.maskInverted;
+ var clipPath = paths.transform(matrix.toNative()!.storage);
+ final inverted = maskInverted || $maskee!.maskInverted;
if (inverted) {
_initInversePath();
// var invPath = Graphics.stageRectPath;
@@ -81,13 +81,13 @@ class GShape extends GDisplayObject {
// invPath = invPath.shift(Offset(rect.x, rect.y));
if (SystemUtils.usingSkia) {
clipPath = ui.Path.combine(
- ui.PathOperation.difference, _inverseHugePath, clipPath);
- canvas.clipPath(clipPath);
+ ui.PathOperation.difference, _inverseHugePath!, clipPath);
+ canvas!.clipPath(clipPath);
} else {
trace('Shape.maskInverted is unsupported in the current platform');
}
} else {
- canvas.clipPath(clipPath);
+ canvas!.clipPath(clipPath);
}
} else {
_graphics?.isMask = isMask;
diff --git a/lib/src/display/sprite.dart b/lib/src/display/sprite.dart
index a6dc3b1..12ae3e2 100644
--- a/lib/src/display/sprite.dart
+++ b/lib/src/display/sprite.dart
@@ -11,31 +11,31 @@ class GSprite extends GDisplayObjectContainer {
static final _sHelperMatrix = GMatrix();
- Graphics _graphics;
+ Graphics? _graphics;
Graphics get graphics => _graphics ??= Graphics();
@override
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect? getBounds(GDisplayObject? targetSpace, [GRect? out]) {
out = super.getBounds(targetSpace, out);
if (_graphics != null) {
_sHelperMatrix.identity();
getTransformationMatrix(targetSpace, _sHelperMatrix);
/// single bounds, all paths as 1 rect.
- final graphicsBounds = _graphics.getBounds();
+ final graphicsBounds = _graphics!.getBounds();
MatrixUtils.getTransformedBoundsRect(
_sHelperMatrix,
graphicsBounds,
graphicsBounds,
);
- out.expandToInclude(graphicsBounds);
+ out!.expandToInclude(graphicsBounds);
}
return out;
}
@override
- GDisplayObject hitTest(GPoint localPoint, [bool useShape = false]) {
+ GDisplayObject? hitTest(GPoint localPoint, [bool useShape = false]) {
if (!visible || !mouseEnabled) return null;
var target = super.hitTest(localPoint);
target ??=
@@ -44,7 +44,7 @@ class GSprite extends GDisplayObjectContainer {
}
@override
- void $applyPaint(ui.Canvas canvas) {
+ void $applyPaint(ui.Canvas? canvas) {
if (!$hasVisibleArea) return;
_graphics?.alpha = worldAlpha;
_graphics?.paint(canvas);
diff --git a/lib/src/display/stage.dart b/lib/src/display/stage.dart
index 7d2797f..791ac7d 100644
--- a/lib/src/display/stage.dart
+++ b/lib/src/display/stage.dart
@@ -36,9 +36,9 @@ class Stage extends GDisplayObjectContainer
return '$runtimeType';
}
- ui.Size _size;
- ui.Paint _backgroundPaint;
- DisplayBoundsDebugger _boundsDebugger;
+ ui.Size? _size;
+ ui.Paint? _backgroundPaint;
+ late DisplayBoundsDebugger _boundsDebugger;
/// Shortcut to access the owner [SceneController].
SceneController get controller => scene.core;
@@ -47,15 +47,15 @@ class Stage extends GDisplayObjectContainer
Signal get onHotReload => controller.onHotReload;
/// The `backgroundPaint` hex color.
- ui.Color get color => _backgroundPaint?.color;
+ ui.Color? get color => _backgroundPaint?.color;
/// Sets the `backgroundPaint` Color.
- set color(ui.Color value) {
+ set color(ui.Color? value) {
if (value == null) {
_backgroundPaint = null;
} else {
_backgroundPaint ??= ui.Paint();
- _backgroundPaint.color = value;
+ _backgroundPaint!.color = value;
}
}
@@ -77,13 +77,13 @@ class Stage extends GDisplayObjectContainer
}
@override
- void paint(ui.Canvas canvas) {
+ void paint(ui.Canvas? canvas) {
/// scene start painting.
if (maskBounds && _stageRectNative != null) {
- canvas.clipRect(_stageRectNative);
+ canvas!.clipRect(_stageRectNative!);
}
if (_backgroundPaint != null) {
- canvas.drawPaint(_backgroundPaint);
+ canvas!.drawPaint(_backgroundPaint!);
}
super.paint(canvas);
if (DisplayBoundsDebugger.debugBoundsMode == DebugBoundsMode.stage) {
@@ -91,11 +91,11 @@ class Stage extends GDisplayObjectContainer
_boundsDebugger.render();
}
if (showBoundsRect) {
- canvas.drawPath(_stageBoundsRectPath, _stageBoundsRectPaint);
+ canvas!.drawPath(_stageBoundsRectPath, _stageBoundsRectPaint);
}
}
- ui.Path _stageBoundsRectPath = ui.Path();
+ final ui.Path _stageBoundsRectPath = ui.Path();
static final ui.Paint _stageBoundsRectPaint = ui.Paint()
..style = ui.PaintingStyle.stroke
..color = kColorBlack
@@ -104,20 +104,25 @@ class Stage extends GDisplayObjectContainer
final GRect _stageRect = GRect();
GRect get stageRect => _stageRect;
- ui.Rect _stageRectNative;
+ ui.Rect? _stageRectNative;
void $initFrameSize(ui.Size value) {
if (value != _size) {
_size = value;
- if (_size.isEmpty) {
+ if (_size!.isEmpty) {
trace(
- "WARNING:\n\tStage has size <= 0 in width or height. If you rely on stage size for a responsive layout or rendering, make sure SceneBuilderWidget() has some child, or the parent is defining the constraints.");
+ // ignore: lines_longer_than_80_chars
+ "WARNING:\n\tStage has size <= 0 in width or height. "
+ "If you rely on stage size for a responsive layout or rendering,"
+ " make sure SceneBuilderWidget() has some child,"
+ " or the parent is defining the constraints.",
+ );
}
_stageRectNative =
- _stageRect.setTo(0, 0, _size.width, _size.height).toNative();
- _stageBoundsRectPath ??= ui.Path();
+ _stageRect.setTo(0, 0, _size!.width, _size!.height).toNative();
+ _stageBoundsRectPath;
_stageBoundsRectPath.reset();
- _stageBoundsRectPath.addRect(_stageRectNative);
+ _stageBoundsRectPath.addRect(_stageRectNative!);
_stageBoundsRectPath.close();
Graphics.updateStageRect(_stageRect);
$onResized?.dispatch();
@@ -126,31 +131,31 @@ class Stage extends GDisplayObjectContainer
/// Access the keyboard instance of the owner `SceneController`,
/// Only available when [SceneConfig.useKeyboard] is true.
- KeyboardManager get keyboard {
- if (scene?.core?.keyboard == null) {
+ KeyboardManager? get keyboard {
+ if (scene.core.keyboard == null) {
throw Exception(
'You need to enable keyboard capture, define useKeyboard=true '
- 'in your SceneController');
+ 'in your SceneController');
}
- return scene?.core?.keyboard;
+ return scene.core.keyboard;
}
/// Access the pointer (mouse or touch info) instance of the
/// owner `SceneController`,
/// Only available when [SceneConfig.usePointer] is true.
- PointerManager get pointer {
- if (scene?.core?.pointer == null) {
+ PointerManager? get pointer {
+ if (scene.core.pointer == null) {
throw 'You need to enable pointer capture, define usePointer=true '
'in your SceneController';
}
- return scene?.core?.pointer;
+ return scene.core.pointer;
}
@override
bool hitTouch(GPoint localPoint, [bool useShape = false]) => true;
@override
- GDisplayObject hitTest(GPoint localPoint, [bool useShapes = false]) {
+ GDisplayObject? hitTest(GPoint localPoint, [bool useShapes = false]) {
if (!visible || !mouseEnabled) return null;
/// location outside stage area, is not accepted.
@@ -184,7 +189,7 @@ class Stage extends GDisplayObjectContainer
}
}
- GRect getStageBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect getStageBounds(GDisplayObject targetSpace, [GRect? out]) {
out ??= GRect();
out.setTo(0, 0, stageWidth, stageHeight);
getTransformationMatrix(targetSpace, _sMatrix);
@@ -209,8 +214,8 @@ class Stage extends GDisplayObjectContainer
void dispose() {
_size = null;
_backgroundPaint = null;
- scene?.core?.pointer?.dispose();
- scene?.core?.keyboard?.dispose();
+ scene.core.pointer?.dispose();
+ scene.core.keyboard?.dispose();
$disposeResizeSignals();
$disposeTickerSignals();
$disposeStagePointerSignals();
@@ -224,22 +229,22 @@ class Stage extends GDisplayObjectContainer
double get height => throw 'Use `stage.stageHeight` instead.';
@override
- set width(double value) => throw 'Cannot set width of stage';
+ set width(double? value) => throw 'Cannot set width of stage';
@override
- set height(double value) => throw 'Cannot set height of stage';
+ set height(double? value) => throw 'Cannot set height of stage';
@override
- set x(double value) => throw 'Cannot set x-coordinate of stage';
+ set x(double? value) => throw 'Cannot set x-coordinate of stage';
@override
- set y(double value) => throw 'Cannot set y-coordinate of stage';
+ set y(double? value) => throw 'Cannot set y-coordinate of stage';
@override
- set scaleX(double value) => throw 'Cannot scale stage';
+ set scaleX(double? value) => throw 'Cannot scale stage';
@override
- set scaleY(double value) => throw 'Cannot scale stage';
+ set scaleY(double? value) => throw 'Cannot scale stage';
@override
set pivotX(double value) => throw 'Cannot pivot stage';
@@ -254,5 +259,5 @@ class Stage extends GDisplayObjectContainer
set skewY(double value) => throw 'Cannot skew stage';
@override
- set rotation(double value) => throw 'Cannot rotate stage';
+ set rotation(double? value) => throw 'Cannot rotate stage';
}
diff --git a/lib/src/display/text.dart b/lib/src/display/text.dart
index d59ac18..0b3bf2c 100644
--- a/lib/src/display/text.dart
+++ b/lib/src/display/text.dart
@@ -3,9 +3,9 @@ import 'package:flutter/painting.dart' as painting;
import '../../graphx.dart';
class GText extends GDisplayObject {
- ui.Paragraph _paragraph;
+ ui.Paragraph? _paragraph;
- Signal _onFontLoaded;
+ Signal? _onFontLoaded;
Signal get onFontLoaded => _onFontLoaded ??= Signal();
@@ -15,7 +15,7 @@ class GText extends GDisplayObject {
final _alphaPaint = ui.Paint();
@override
- GRect getBounds(GDisplayObject targetSpace, [GRect out]) {
+ GRect getBounds(GDisplayObject? targetSpace, [GRect? out]) {
validate();
out ??= GRect();
out.setTo(0, 0, intrinsicWidth, textHeight);
@@ -42,27 +42,27 @@ class GText extends GDisplayObject {
// return r;
// }
- ui.Paragraph get paragraph => _paragraph;
+ ui.Paragraph? get paragraph => _paragraph;
// ui.TextStyle _style;
- painting.TextStyle _style;
+ painting.TextStyle? _style;
- double _width;
+ double _width = 0.0;
- ui.ParagraphBuilder _builder;
- ui.ParagraphStyle _paragraphStyle;
+ late ui.ParagraphBuilder _builder;
+ ui.ParagraphStyle? _paragraphStyle;
bool _invalidSize = true;
bool _invalidBuilder = true;
- String _text;
- ui.Color backgroundColor;
+ String? _text;
+ ui.Color? backgroundColor;
- ui.Color get color {
- return _style.color;
+ ui.Color? get color {
+ return _style!.color;
}
- set color(ui.Color value) {
- _style = _style.copyWith(color: value);
+ set color(ui.Color? value) {
+ _style = _style!.copyWith(color: value);
_invalidBuilder = true;
// _invalidateBuilder();
}
@@ -77,7 +77,7 @@ class GText extends GDisplayObject {
String get text => _text ?? '';
- set text(String value) {
+ set text(String? value) {
if (_text == value) return;
_text = value ?? '';
_invalidBuilder = true;
@@ -88,7 +88,7 @@ class GText extends GDisplayObject {
double get width => _width;
@override
- set width(double value) {
+ set width(double? value) {
if (value == null || _width == value) return;
_width = value;
_invalidSize = true;
@@ -158,15 +158,15 @@ class GText extends GDisplayObject {
// }
GText({
- String text,
- ui.ParagraphStyle paragraphStyle,
- painting.TextStyle textStyle,
+ String? text,
+ ui.ParagraphStyle? paragraphStyle,
+ painting.TextStyle? textStyle,
double width = double.infinity,
}) {
- painting.PaintingBinding.instance.systemFonts.addListener(_fontLoaded);
+ painting.PaintingBinding.instance!.systemFonts.addListener(_fontLoaded);
this.text = text;
- _width = width ?? double.infinity;
+ _width = width;
_invalidBuilder = true;
_invalidSize = true;
setTextStyle(textStyle ?? defaultTextStyle);
@@ -182,32 +182,32 @@ class GText extends GDisplayObject {
@override
void dispose() {
super.dispose();
- painting.PaintingBinding.instance.systemFonts.removeListener(_fontLoaded);
+ painting.PaintingBinding.instance!.systemFonts.removeListener(_fontLoaded);
_onFontLoaded?.removeAll();
_onFontLoaded = null;
}
void setTextStyle(painting.TextStyle style) {
- if (style == null || _style == style) return;
+ if (_style == style) return;
_style = style;
_invalidBuilder = true;
}
- painting.TextStyle getTextStyle() => _style;
+ painting.TextStyle? getTextStyle() => _style;
- ui.ParagraphStyle getParagraphStyle() => _paragraphStyle;
+ ui.ParagraphStyle? getParagraphStyle() => _paragraphStyle;
void setParagraphStyle(ui.ParagraphStyle style) {
- if (style == null || _paragraphStyle == style) return;
+ if (_paragraphStyle == style) return;
_paragraphStyle = style;
_invalidBuilder = true;
}
void _invalidateBuilder() {
_paragraphStyle ??= defaultParagraphStyle;
- _builder = ui.ParagraphBuilder(_paragraphStyle);
+ _builder = ui.ParagraphBuilder(_paragraphStyle!);
// if (_style != null) _builder.pushStyle(_style);
- if (_style != null) _builder.pushStyle(_style.getTextStyle());
+ if (_style != null) _builder.pushStyle(_style!.getTextStyle());
_builder.addText(_text ?? '');
_paragraph = _builder.build();
_invalidBuilder = false;
@@ -227,14 +227,14 @@ class GText extends GDisplayObject {
/// applies the painting after the DisplayObject transformations.
/// Should be overriden by subclasses.
@override
- void $applyPaint(ui.Canvas canvas) {
+ void $applyPaint(ui.Canvas? canvas) {
super.$applyPaint(canvas);
if (_text == '') return;
validate();
- if (backgroundColor != null && backgroundColor.alpha > 0) {
- canvas.drawRect(
+ if (backgroundColor != null && backgroundColor!.alpha > 0) {
+ canvas!.drawRect(
ui.Rect.fromLTWH(0, 0, intrinsicWidth, textHeight),
- ui.Paint()..color = backgroundColor,
+ ui.Paint()..color = backgroundColor!,
);
}
if (_paragraph != null) {
@@ -243,18 +243,18 @@ class GText extends GDisplayObject {
// final alphaPaint = PainterUtils.getAlphaPaint($alpha);
// final alphaPaint = _alphaPaint;
// var bounds = Rect.fromLTWH(0, 0, textWidth, textHeight);
- canvas.saveLayer(null, _alphaPaint);
- canvas.drawParagraph(_paragraph, ui.Offset.zero);
+ canvas!.saveLayer(null, _alphaPaint);
+ canvas.drawParagraph(_paragraph!, ui.Offset.zero);
canvas.restore();
} else {
- canvas.drawParagraph(_paragraph, ui.Offset.zero);
+ canvas!.drawParagraph(_paragraph!, ui.Offset.zero);
}
}
}
@override
set alpha(double value) {
- final changed = value != $alpha && value != null;
+ final changed = value != $alpha;
super.alpha = value;
if (changed) {
_alphaPaint.color = _alphaPaint.color.withOpacity($alpha);
@@ -278,34 +278,33 @@ class GText extends GDisplayObject {
/// Factory method to simplify the initialization of a StaticText instance.
/// You can pass the parent object directly in the `doc` parameter.
static GText build({
- String text,
- GDisplayObjectContainer doc,
- ui.Color color,
+ String? text,
+ GDisplayObjectContainer? doc,
+ ui.Color? color,
double w = double.infinity,
- ui.TextDecoration decoration,
- ui.Color decorationColor,
- ui.TextDecorationStyle decorationStyle,
- double decorationThickness,
- ui.FontWeight fontWeight,
- ui.FontStyle fontStyle,
- ui.TextBaseline textBaseline,
- String fontFamily,
- List fontFamilyFallback,
- double fontSize,
- double letterSpacing,
- double wordSpacing,
- double height,
- ui.Locale locale,
- ui.Paint background,
- ui.Paint foreground,
- String ellipsis,
- int maxLines,
- List shadows,
- List fontFeatures,
+ ui.TextDecoration? decoration,
+ ui.Color? decorationColor,
+ ui.TextDecorationStyle? decorationStyle,
+ double? decorationThickness,
+ ui.FontWeight? fontWeight,
+ ui.FontStyle? fontStyle,
+ ui.TextBaseline? textBaseline,
+ String? fontFamily,
+ List? fontFamilyFallback,
+ double? fontSize,
+ double? letterSpacing,
+ double? wordSpacing,
+ double? height,
+ ui.Locale? locale,
+ ui.Paint? background,
+ ui.Paint? foreground,
+ String? ellipsis,
+ int? maxLines,
+ List? shadows,
+ List? fontFeatures,
ui.TextAlign textAlign = ui.TextAlign.left,
ui.TextDirection direction = ui.TextDirection.ltr,
}) {
- w ??= double.infinity;
if (w == double.infinity && textAlign != ui.TextAlign.left) {
throw "[StaticText] To use $textAlign you need to specify the width";
}
@@ -335,10 +334,10 @@ class GText extends GDisplayObject {
width: w,
textStyle: style,
paragraphStyle: ui.ParagraphStyle(
- textAlign: textAlign,
- textDirection: direction,
maxLines: maxLines,
ellipsis: ellipsis,
+ textAlign: textAlign,
+ textDirection: direction,
),
);
doc?.addChild(tf);
diff --git a/lib/src/events/callback_params.dart b/lib/src/events/callback_params.dart
index c1b2874..491c1d0 100644
--- a/lib/src/events/callback_params.dart
+++ b/lib/src/events/callback_params.dart
@@ -1,14 +1,14 @@
class CallbackParams {
- List positional;
+ List? positional;
/// Symbol is represented by the literal # in Dart.
/// So, if u need `({String name, int count})` in named parameters.
/// You'd use {#name: 'roi', #count: 10}.
- Map named;
+ Map? named;
CallbackParams([this.positional, this.named]);
- static CallbackParams parse(Object args) {
+ static CallbackParams? parse(Object? args) {
if (args == null) return null;
if (args is CallbackParams) return args;
@@ -16,7 +16,7 @@ class CallbackParams {
if (args is List) {
return CallbackParams(args);
} else if (args is Map) {
- return CallbackParams(null, args);
+ return CallbackParams(null, args as Map?);
}
return CallbackParams([args]);
diff --git a/lib/src/events/keyboard_data.dart b/lib/src/events/keyboard_data.dart
index afa5809..0a30697 100644
--- a/lib/src/events/keyboard_data.dart
+++ b/lib/src/events/keyboard_data.dart
@@ -4,36 +4,36 @@ import 'package:flutter/widgets.dart';
enum KeyEventType { down, up }
class KeyboardEventData {
- final KeyEventType type;
- final RawKeyEvent rawEvent;
+ final KeyEventType? type;
+ final RawKeyEvent? rawEvent;
KeyboardEventData({this.type, this.rawEvent});
bool isPressed(LogicalKeyboardKey key) {
- return rawEvent.isKeyPressed(key);
+ return rawEvent!.isKeyPressed(key);
}
bool isKey(LogicalKeyboardKey key) {
- return rawEvent.logicalKey == key;
+ return rawEvent!.logicalKey == key;
}
}
extension MyKeyEventExt on KeyboardEventData {
bool get arrowLeft {
// return rawEvent.isKeyPressed(LogicalKeyboardKey.arrowLeft);
- return rawEvent.logicalKey == LogicalKeyboardKey.arrowLeft;
+ return rawEvent!.logicalKey == LogicalKeyboardKey.arrowLeft;
}
bool get arrowRight {
- return rawEvent.logicalKey == LogicalKeyboardKey.arrowRight;
+ return rawEvent!.logicalKey == LogicalKeyboardKey.arrowRight;
// return rawEvent.isKeyPressed(LogicalKeyboardKey.arrowRight);
}
bool get arrowUp {
- return rawEvent.logicalKey == LogicalKeyboardKey.arrowUp;
+ return rawEvent!.logicalKey == LogicalKeyboardKey.arrowUp;
}
bool get arrowDown {
- return rawEvent.logicalKey == LogicalKeyboardKey.arrowDown;
+ return rawEvent!.logicalKey == LogicalKeyboardKey.arrowDown;
}
}
diff --git a/lib/src/events/mixins.dart b/lib/src/events/mixins.dart
index d5c5753..e62751d 100644
--- a/lib/src/events/mixins.dart
+++ b/lib/src/events/mixins.dart
@@ -27,7 +27,7 @@ mixin EventDispatcherMixin implements Listenable {
}
mixin TickerSignalMixin {
- EventSignal $onEnterFrame;
+ EventSignal? $onEnterFrame;
EventSignal get onEnterFrame =>
$onEnterFrame ??= EventSignal();
@@ -39,7 +39,7 @@ mixin TickerSignalMixin {
}
mixin ResizeSignalMixin {
- Signal $onResized;
+ Signal? $onResized;
Signal get onResized => $onResized ??= Signal();
void $disposeResizeSignals() {
@@ -49,19 +49,19 @@ mixin ResizeSignalMixin {
}
mixin DisplayListSignalsMixin {
- Signal $onAdded;
+ Signal? $onAdded;
Signal get onAdded => $onAdded ??= Signal();
- Signal $onRemoved;
+ Signal? $onRemoved;
Signal get onRemoved => $onRemoved ??= Signal();
- Signal $onRemovedFromStage;
+ Signal? $onRemovedFromStage;
Signal get onRemovedFromStage => $onRemovedFromStage ??= Signal();
- Signal $onAddedToStage;
+ Signal? $onAddedToStage;
Signal get onAddedToStage => $onAddedToStage ??= Signal();
@@ -78,13 +78,14 @@ mixin DisplayListSignalsMixin {
}
mixin RenderSignalMixin {
- EventSignal