From 2ae2b4875303698ea87668ce5d75ffd9666a047e Mon Sep 17 00:00:00 2001 From: Plague Fox Date: Fri, 4 Aug 2023 13:28:48 +0400 Subject: [PATCH] Patch native platform --- .vscode/launch.json | 13 +- example/config/development.json | 5 + example/lib/main.dart | 150 ++++--------------- example/linux/my_application.cc | 52 ++++--- example/macos/Runner/AppDelegate.swift | 3 +- example/macos/Runner/MainFlutterWindow.swift | 6 + example/windows/runner/flutter_window.cpp | 10 +- example/windows/runner/win32_window.cpp | 136 ++++++++--------- 8 files changed, 145 insertions(+), 230 deletions(-) create mode 100644 example/config/development.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 5cbb077..532c5dc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,20 +2,19 @@ "version": "0.2.0", "configurations": [ { - "name": "Example", + "name": "Example (dev)", "request": "launch", "type": "dart", - "program": "example", + "flutterMode": "debug", + "cwd": "${workspaceFolder}/example", + "program": "lib/main.dart", "env": { - "ENVIRONMENT": "local" + "ENVIRONMENT": "development" }, "console": "debugConsole", "runTestsOnDevice": false, "toolArgs": [], - "args": [ - // "--token=X.Y.Z", - "--verbose" - ] + "args": ["--dart-define-from-file=config/development.json"] } ] } diff --git a/example/config/development.json b/example/config/development.json new file mode 100644 index 0000000..f1cdc5e --- /dev/null +++ b/example/config/development.json @@ -0,0 +1,5 @@ +{ + "ENVIRONMENT": "development", + "CENTRIFUGE_BASE_URL": "http://localhost:8000", + "CENTRIFUGE_TIMEOUT": 8000 +} diff --git a/example/lib/main.dart b/example/lib/main.dart index dda5554..f532f7c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,125 +1,33 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a blue toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". +import 'dart:async'; - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, +import 'package:flutter/material.dart'; +import 'package:l/l.dart'; +import 'package:spinifyapp/src/common/util/logger_util.dart'; +import 'package:spinifyapp/src/common/widget/app.dart'; +import 'package:spinifyapp/src/feature/dependencies/initialization/initialization.dart'; +import 'package:spinifyapp/src/feature/dependencies/widget/dependencies_scope.dart'; +import 'package:spinifyapp/src/feature/dependencies/widget/initialization_splash_screen.dart'; + +void main() => l.capture( + () => runZonedGuarded( + () { + final initialization = InitializationExecutor(); + runApp( + DependenciesScope( + initialization: initialization(), + splashScreen: InitializationSplashScreen( + progress: initialization, + ), + child: const App(), ), - ], - ), + ); + }, + l.e, + ), + const LogOptions( + handlePrint: true, + messageFormatting: LoggerUtil.messageFormatting, + outputInRelease: false, + printColors: true, ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ); - } -} diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc index 1283c30..c44e7ba 100644 --- a/example/linux/my_application.cc +++ b/example/linux/my_application.cc @@ -9,15 +9,15 @@ struct _MyApplication { GtkApplication parent_instance; - char** dart_entrypoint_arguments; + 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 = +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 @@ -29,16 +29,16 @@ static void my_application_activate(GApplication* application) { // if future cases occur). gboolean use_header_bar = TRUE; #ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); + 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); + 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()); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); gtk_header_bar_set_title(header_bar, "spinifyapp"); gtk_header_bar_set_show_close_button(header_bar, TRUE); @@ -48,12 +48,14 @@ static void my_application_activate(GApplication* application) { } gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); + // gtk_widget_show(GTK_WIDGET(window)); + gtk_widget_realize(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); - FlView* view = fl_view_new(project); + FlView *view = fl_view_new(project); gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); @@ -63,16 +65,18 @@ static void my_application_activate(GApplication* application) { } // Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); +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_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; } g_application_activate(application); @@ -82,23 +86,23 @@ static gboolean my_application_local_command_line(GApplication* application, gch } // Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); +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) { +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_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) {} +static void my_application_init(MyApplication *self) {} -MyApplication* my_application_new() { +MyApplication *my_application_new() { return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); } diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index d53ef64..a8565c3 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -4,6 +4,7 @@ import FlutterMacOS @NSApplicationMain class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true + //return true + return false } } diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift index 3cc05eb..1ca7bf0 100644 --- a/example/macos/Runner/MainFlutterWindow.swift +++ b/example/macos/Runner/MainFlutterWindow.swift @@ -1,5 +1,6 @@ import Cocoa import FlutterMacOS +import window_manager class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -12,4 +13,9 @@ class MainFlutterWindow: NSWindow { super.awakeFromNib() } + + override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { + super.order(place, relativeTo: otherWin) + hiddenWindowAtLaunch() + } } diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp index b25e363..0e4c5f5 100644 --- a/example/windows/runner/flutter_window.cpp +++ b/example/windows/runner/flutter_window.cpp @@ -4,7 +4,7 @@ #include "flutter/generated_plugin_registrant.h" -FlutterWindow::FlutterWindow(const flutter::DartProject& project) +FlutterWindow::FlutterWindow(const flutter::DartProject &project) : project_(project) {} FlutterWindow::~FlutterWindow() {} @@ -28,7 +28,7 @@ bool FlutterWindow::OnCreate() { SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); + // this->Show() }); return true; @@ -57,9 +57,9 @@ FlutterWindow::MessageHandler(HWND hwnd, UINT const message, } switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; } return Win32Window::MessageHandler(hwnd, message, wparam, lparam); diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp index 60608d0..145247b 100644 --- a/example/windows/runner/win32_window.cpp +++ b/example/windows/runner/win32_window.cpp @@ -11,7 +11,8 @@ namespace { /// /// Redefined in case the developer's machine has a Windows SDK older than /// version 10.0.22000.0. -/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +/// See: +/// https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #endif @@ -23,8 +24,9 @@ constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; /// A value of 0 indicates apps should use dark mode. A non-zero or missing /// value indicates apps should use light mode. constexpr const wchar_t kGetPreferredBrightnessRegKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; -constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = + L"AppsUseLightTheme"; // The number of Win32Window objects that currently exist. static int g_active_window_count = 0; @@ -45,7 +47,7 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) { return; } auto enable_non_client_dpi_scaling = - reinterpret_cast( + reinterpret_cast( GetProcAddress(user32_module, "EnableNonClientDpiScaling")); if (enable_non_client_dpi_scaling != nullptr) { enable_non_client_dpi_scaling(hwnd); @@ -53,15 +55,15 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) { FreeLibrary(user32_module); } -} // namespace +} // namespace // Manages the Win32Window's window class registration. class WindowClassRegistrar { - public: +public: ~WindowClassRegistrar() = default; // Returns the singleton registrar instance. - static WindowClassRegistrar* GetInstance() { + static WindowClassRegistrar *GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); } @@ -70,23 +72,23 @@ class WindowClassRegistrar { // Returns the name of the window class, registering the class if it hasn't // previously been registered. - const wchar_t* GetWindowClass(); + const wchar_t *GetWindowClass(); // Unregisters the window class. Should only be called if there are no // instances of the window. void UnregisterWindowClass(); - private: +private: WindowClassRegistrar() = default; - static WindowClassRegistrar* instance_; + static WindowClassRegistrar *instance_; bool class_registered_ = false; }; -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; +WindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr; -const wchar_t* WindowClassRegistrar::GetWindowClass() { +const wchar_t *WindowClassRegistrar::GetWindowClass() { if (!class_registered_) { WNDCLASS window_class{}; window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); @@ -111,21 +113,18 @@ void WindowClassRegistrar::UnregisterWindowClass() { class_registered_ = false; } -Win32Window::Win32Window() { - ++g_active_window_count; -} +Win32Window::Win32Window() { ++g_active_window_count; } Win32Window::~Win32Window() { --g_active_window_count; Destroy(); } -bool Win32Window::Create(const std::wstring& title, - const Point& origin, - const Size& size) { +bool Win32Window::Create(const std::wstring &title, const Point &origin, + const Size &size) { Destroy(); - const wchar_t* window_class = + const wchar_t *window_class = WindowClassRegistrar::GetInstance()->GetWindowClass(); const POINT target_point = {static_cast(origin.x), @@ -149,24 +148,21 @@ bool Win32Window::Create(const std::wstring& title, return OnCreate(); } -bool Win32Window::Show() { - return ShowWindow(window_handle_, SW_SHOWNORMAL); -} +bool Win32Window::Show() { return ShowWindow(window_handle_, SW_SHOWNORMAL); } // static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, +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); + auto window_struct = reinterpret_cast(lparam); SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(window_struct->lpCreateParams)); - auto that = static_cast(window_struct->lpCreateParams); + auto that = static_cast(window_struct->lpCreateParams); EnableFullDpiSupportIfAvailable(window); that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { + } else if (Win32Window *that = GetThisFromHandle(window)) { return that->MessageHandler(window, message, wparam, lparam); } @@ -174,48 +170,46 @@ LRESULT CALLBACK Win32Window::WndProc(HWND const window, } LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, +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_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(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; + 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; + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; - case WM_DWMCOLORIZATIONCOLORCHANGED: - UpdateTheme(hwnd); - return 0; + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; } return DefWindowProc(window_handle_, message, wparam, lparam); @@ -233,8 +227,8 @@ void Win32Window::Destroy() { } } -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( +Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( GetWindowLongPtr(window, GWLP_USERDATA)); } @@ -255,9 +249,7 @@ RECT Win32Window::GetClientArea() { return frame; } -HWND Win32Window::GetHandle() { - return window_handle_; -} +HWND Win32Window::GetHandle() { return window_handle_; } void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; @@ -275,10 +267,10 @@ void Win32Window::OnDestroy() { void Win32Window::UpdateTheme(HWND const window) { DWORD light_mode; DWORD light_mode_size = sizeof(light_mode); - LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, - kGetPreferredBrightnessRegValue, - RRF_RT_REG_DWORD, nullptr, &light_mode, - &light_mode_size); + LSTATUS result = + RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, + &light_mode, &light_mode_size); if (result == ERROR_SUCCESS) { BOOL enable_dark_mode = light_mode == 0;