mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support windows supabase deeplink callback (#3133)
This commit is contained in:
parent
fbb1753350
commit
95a938657b
@ -1,15 +1,29 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:url_protocol/url_protocol.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../startup.dart';
|
||||
|
||||
// ONLY supports in macOS and Windows now.
|
||||
//
|
||||
// If you need to update the schema, please update the following files:
|
||||
// - appflowy_flutter/macos/Runner/Info.plist (macOS)
|
||||
// - the callback url in Supabase dashboard
|
||||
const appflowyDeepLinkSchema = 'appflowy-flutter';
|
||||
const supabaseLoginCallback = '$appflowyDeepLinkSchema://login-callback';
|
||||
|
||||
bool isSupabaseInitialized = false;
|
||||
const hiveBoxName = 'appflowy_supabase_authentication';
|
||||
|
||||
// Used to store the session of the supabase in case of the user switch the different folder.
|
||||
Supabase? supabase;
|
||||
|
||||
class InitSupabaseTask extends LaunchTask {
|
||||
@override
|
||||
Future<void> initialize(LaunchContext context) async {
|
||||
@ -20,7 +34,15 @@ class InitSupabaseTask extends LaunchTask {
|
||||
if (isSupabaseInitialized) {
|
||||
return;
|
||||
}
|
||||
await Supabase.initialize(
|
||||
|
||||
// register deep link for Windows
|
||||
if (Platform.isWindows) {
|
||||
registerProtocolHandler(appflowyDeepLinkSchema);
|
||||
}
|
||||
|
||||
supabase?.dispose();
|
||||
supabase = null;
|
||||
supabase = await Supabase.initialize(
|
||||
url: Env.supabaseUrl,
|
||||
anonKey: Env.supabaseAnonKey,
|
||||
debug: kDebugMode,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/startup/tasks/prelude.dart';
|
||||
import 'package:appflowy/user/application/auth/appflowy_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
@ -13,9 +14,6 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'auth_error.dart';
|
||||
|
||||
// can't use underscore here.
|
||||
const loginCallback = 'io.appflowy.appflowy-flutter://login-callback';
|
||||
|
||||
class SupabaseAuthService implements AuthService {
|
||||
SupabaseAuthService();
|
||||
|
||||
@ -123,7 +121,7 @@ class SupabaseAuthService implements AuthService {
|
||||
final response = await _auth.signInWithOAuth(
|
||||
provider,
|
||||
queryParams: queryParamsForProvider(provider),
|
||||
redirectTo: loginCallback,
|
||||
redirectTo: supabaseLoginCallback,
|
||||
);
|
||||
if (!response) {
|
||||
completer.complete(left(AuthError.supabaseSignInWithOauthError));
|
||||
@ -171,7 +169,7 @@ class SupabaseAuthService implements AuthService {
|
||||
|
||||
await _auth.signInWithOtp(
|
||||
email: email,
|
||||
emailRedirectTo: kIsWeb ? null : loginCallback,
|
||||
emailRedirectTo: kIsWeb ? null : supabaseLoginCallback,
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>io.appflowy.appflowy-flutter</string>
|
||||
<string>appflowy-flutter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
@ -1589,6 +1589,15 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
url_protocol:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "77a84201ed8ca50082f4248f3a373d053b1c0462"
|
||||
url: "https://github.com/LucasXu0/flutter_url_protocol.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -98,6 +98,10 @@ dependencies:
|
||||
supabase_flutter: ^1.10.4
|
||||
envied: ^0.3.0+3
|
||||
dotted_border: ^2.0.0+3
|
||||
url_protocol:
|
||||
git:
|
||||
url: https://github.com/LucasXu0/flutter_url_protocol.git
|
||||
commit: 77a8420
|
||||
hive: ^2.2.3
|
||||
hive_flutter: ^1.1.0
|
||||
|
||||
|
@ -8,16 +8,6 @@
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command)
|
||||
{
|
||||
HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L"AppFlowyMutex");
|
||||
HWND handle = FindWindowA(NULL, "AppFlowy");
|
||||
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
|
||||
GetWindowPlacement(handle, &place);
|
||||
ShowWindow(handle, SW_NORMAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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())
|
||||
@ -53,6 +43,5 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
}
|
||||
|
||||
::CoUninitialize();
|
||||
ReleaseMutex(hMutexInstance);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -4,47 +4,57 @@
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
namespace {
|
||||
#include "app_links/app_links_plugin_c_api.h"
|
||||
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
namespace
|
||||
{
|
||||
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
|
||||
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
|
||||
// Scale helper to convert logical scaler values to physical using passed in
|
||||
// scale factor
|
||||
int Scale(int source, double scale_factor) {
|
||||
return static_cast<int>(source * scale_factor);
|
||||
}
|
||||
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||
|
||||
// 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;
|
||||
// Scale helper to convert logical scaler values to physical using passed in
|
||||
// scale factor
|
||||
int Scale(int source, double scale_factor)
|
||||
{
|
||||
return static_cast<int>(source * scale_factor);
|
||||
}
|
||||
auto enable_non_client_dpi_scaling =
|
||||
reinterpret_cast<EnableNonClientDpiScaling*>(
|
||||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||
if (enable_non_client_dpi_scaling != nullptr) {
|
||||
enable_non_client_dpi_scaling(hwnd);
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
// 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<EnableNonClientDpiScaling *>(
|
||||
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:
|
||||
class WindowClassRegistrar
|
||||
{
|
||||
public:
|
||||
~WindowClassRegistrar() = default;
|
||||
|
||||
// Returns the singleton registar instance.
|
||||
static WindowClassRegistrar* GetInstance() {
|
||||
if (!instance_) {
|
||||
static WindowClassRegistrar *GetInstance()
|
||||
{
|
||||
if (!instance_)
|
||||
{
|
||||
instance_ = new WindowClassRegistrar();
|
||||
}
|
||||
return instance_;
|
||||
@ -52,24 +62,26 @@ 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() {
|
||||
if (!class_registered_) {
|
||||
const wchar_t *WindowClassRegistrar::GetWindowClass()
|
||||
{
|
||||
if (!class_registered_)
|
||||
{
|
||||
WNDCLASS window_class{};
|
||||
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
window_class.lpszClassName = kWindowClassName;
|
||||
@ -88,26 +100,35 @@ const wchar_t* WindowClassRegistrar::GetWindowClass() {
|
||||
return kWindowClassName;
|
||||
}
|
||||
|
||||
void WindowClassRegistrar::UnregisterWindowClass() {
|
||||
void WindowClassRegistrar::UnregisterWindowClass()
|
||||
{
|
||||
UnregisterClass(kWindowClassName, nullptr);
|
||||
class_registered_ = false;
|
||||
}
|
||||
|
||||
Win32Window::Win32Window() {
|
||||
Win32Window::Win32Window()
|
||||
{
|
||||
++g_active_window_count;
|
||||
}
|
||||
|
||||
Win32Window::~Win32Window() {
|
||||
Win32Window::~Win32Window()
|
||||
{
|
||||
--g_active_window_count;
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size) {
|
||||
bool Win32Window::CreateAndShow(const std::wstring &title,
|
||||
const Point &origin,
|
||||
const Size &size)
|
||||
{
|
||||
if (SendAppLinkToInstance(title))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Destroy();
|
||||
|
||||
const wchar_t* window_class =
|
||||
const wchar_t *window_class =
|
||||
WindowClassRegistrar::GetInstance()->GetWindowClass();
|
||||
|
||||
const POINT target_point = {static_cast<LONG>(origin.x),
|
||||
@ -122,27 +143,69 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
||||
if (!window) {
|
||||
if (!window)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return OnCreate();
|
||||
}
|
||||
|
||||
bool Win32Window::SendAppLinkToInstance(const std::wstring &title)
|
||||
{
|
||||
// Find our exact window
|
||||
HWND hwnd = ::FindWindow(kWindowClassName, title.c_str());
|
||||
|
||||
if (hwnd)
|
||||
{
|
||||
// Dispatch new link to current window
|
||||
SendAppLink(hwnd);
|
||||
|
||||
// (Optional) Restore our window to front in same state
|
||||
WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)};
|
||||
GetWindowPlacement(hwnd, &place);
|
||||
|
||||
switch (place.showCmd)
|
||||
{
|
||||
case SW_SHOWMAXIMIZED:
|
||||
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
|
||||
break;
|
||||
case SW_SHOWMINIMIZED:
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
break;
|
||||
default:
|
||||
ShowWindow(hwnd, SW_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
|
||||
SetForegroundWindow(hwnd);
|
||||
|
||||
// Window has been found, don't create another one.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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<CREATESTRUCT*>(lparam);
|
||||
LPARAM const lparam) noexcept
|
||||
{
|
||||
if (message == WM_NCCREATE)
|
||||
{
|
||||
auto window_struct = reinterpret_cast<CREATESTRUCT *>(lparam);
|
||||
SetWindowLongPtr(window, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
|
||||
|
||||
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
|
||||
auto that = static_cast<Win32Window *>(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);
|
||||
}
|
||||
|
||||
@ -153,64 +216,76 @@ 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<RECT*>(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;
|
||||
LPARAM const lparam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
window_handle_ = nullptr;
|
||||
Destroy();
|
||||
if (quit_on_close_)
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_ACTIVATE:
|
||||
if (child_content_ != nullptr) {
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
return 0;
|
||||
case WM_DPICHANGED:
|
||||
{
|
||||
auto newRectSize = reinterpret_cast<RECT *>(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() {
|
||||
void Win32Window::Destroy()
|
||||
{
|
||||
OnDestroy();
|
||||
|
||||
if (window_handle_) {
|
||||
if (window_handle_)
|
||||
{
|
||||
DestroyWindow(window_handle_);
|
||||
window_handle_ = nullptr;
|
||||
}
|
||||
if (g_active_window_count == 0) {
|
||||
if (g_active_window_count == 0)
|
||||
{
|
||||
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
|
||||
}
|
||||
}
|
||||
|
||||
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
|
||||
return reinterpret_cast<Win32Window*>(
|
||||
Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept
|
||||
{
|
||||
return reinterpret_cast<Win32Window *>(
|
||||
GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
void Win32Window::SetChildContent(HWND content) {
|
||||
void Win32Window::SetChildContent(HWND content)
|
||||
{
|
||||
child_content_ = content;
|
||||
SetParent(content, window_handle_);
|
||||
RECT frame = GetClientArea();
|
||||
@ -221,25 +296,30 @@ void Win32Window::SetChildContent(HWND content) {
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
|
||||
RECT Win32Window::GetClientArea() {
|
||||
RECT Win32Window::GetClientArea()
|
||||
{
|
||||
RECT frame;
|
||||
GetClientRect(window_handle_, &frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
HWND Win32Window::GetHandle() {
|
||||
HWND Win32Window::GetHandle()
|
||||
{
|
||||
return window_handle_;
|
||||
}
|
||||
|
||||
void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
||||
void Win32Window::SetQuitOnClose(bool quit_on_close)
|
||||
{
|
||||
quit_on_close_ = quit_on_close;
|
||||
}
|
||||
|
||||
bool Win32Window::OnCreate() {
|
||||
bool Win32Window::OnCreate()
|
||||
{
|
||||
// No-op; provided for subclasses.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32Window::OnDestroy() {
|
||||
void Win32Window::OnDestroy()
|
||||
{
|
||||
// No-op; provided for subclasses.
|
||||
}
|
||||
|
@ -10,15 +10,18 @@
|
||||
// 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 {
|
||||
class Win32Window
|
||||
{
|
||||
public:
|
||||
struct Point
|
||||
{
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
struct Size {
|
||||
struct Size
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
@ -34,9 +37,9 @@ class Win32Window {
|
||||
// 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);
|
||||
bool CreateAndShow(const std::wstring &title,
|
||||
const Point &origin,
|
||||
const Size &size);
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
@ -54,7 +57,7 @@ class Win32Window {
|
||||
// Return a RECT representing the bounds of the current client area.
|
||||
RECT GetClientArea();
|
||||
|
||||
protected:
|
||||
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.
|
||||
@ -70,7 +73,7 @@ class Win32Window {
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
|
||||
private:
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
@ -84,7 +87,11 @@ class Win32Window {
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
static Win32Window *GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
// Dispatches link if any.
|
||||
// This method enables our app to be with a single instance too.
|
||||
bool SendAppLinkToInstance(const std::wstring &title);
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
@ -95,4 +102,4 @@ class Win32Window {
|
||||
HWND child_content_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
|
Loading…
Reference in New Issue
Block a user