Skip to content

Commit

Permalink
Merge pull request #16 from alexmercerind/window-restore-fix-api-change
Browse files Browse the repository at this point in the history
refactor: improve NativeViewContainer API & fix: window restore animation
  • Loading branch information
alexmercerind authored May 25, 2022
2 parents af2374d + aac22da commit 5e6360b
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 64 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 0.0.2

- chore: switch to MIT license.
- fix: window restore animation after clicking on taskbar icon (@alexmercerind).
- refactor: plugin initialization (@alexmercerind).

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Add following lines to your `windows/runner/main.cpp` file:
```diff
window.SetQuitOnClose(true);

+ flutternativeview::CreateNativeViewContainer();
+ flutternativeview::NativeViewContainer::GetInstance()->Create();

::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
Expand Down Expand Up @@ -155,11 +155,12 @@ controller.dispose();
- Windows 10 & higher support.
- Proper disposing of `HWND` and instances.
- Semi transparent `Widget`s on top of `NativeView`.
- Customizable hit-test i.e. optional interactability with the `NativeView`s.
- Placement of `NativeView`s inside scrollables like `ListView`s.
- [UNSTABLE] Customizable hit-test i.e. optional interactability with the `NativeView`s.

#### Under Progress

- Stable support for interactability with the `NativeView`s [maybe switching to programmatic approach].
- Support for older Windows versions.
- Defining z-order for each `NativeViewController`.
- Finalized API.
Expand Down
102 changes: 52 additions & 50 deletions core/native_view_container.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,58 @@

namespace flutternativeview {

constexpr const auto kClassName = L"FLUTTER_NATIVE_VIEW";
constexpr const auto kWindowName = L"flutter_native_view";
NativeViewContainer* NativeViewContainer::GetInstance() {
return instance_.get();
}

HWND NativeViewContainer::Create() {
auto window_class = WNDCLASSEX{};
::SecureZeroMemory(&window_class, sizeof(window_class));
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = WindowProc;
window_class.hInstance = 0;
window_class.lpszClassName = kClassName;
window_class.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
window_class.hbrBackground = ::CreateSolidBrush(0);
::RegisterClassExW(&window_class);
handle_ =
::CreateWindow(kClassName, kWindowName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
// Disable DWM animations on |native_view_container|.
auto disable_window_transitions = TRUE;
DwmSetWindowAttribute(handle_, DWMWA_TRANSITIONS_FORCEDISABLED,
&disable_window_transitions,
sizeof(disable_window_transitions));
return handle_;
}

static HWND native_view_container = nullptr;
HWND NativeViewContainer::Get(HWND window) {
RECT window_rect;
::GetWindowRect(window, &window_rect);
::SetWindowPos(handle_, window, window_rect.left, window_rect.top,
window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
::SetWindowLongPtr(handle_, GWLP_USERDATA, reinterpret_cast<LONG>(window));
// Remove taskbar entry.
// Using |ITaskbarList3| available on Windows 7 or higher. Modification to
// |native_view_container|'s |GWL_STYLE| OR |GWL_EX_STYLE| made underlying
// |HWND| obvious to user.
ITaskbarList3* taskbar = nullptr;
::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&taskbar));
taskbar->DeleteTab(handle_);
taskbar->Release();
::ShowWindow(handle_, SW_SHOWNOACTIVATE);
::SetFocus(window);
return handle_;
}

LRESULT CALLBACK NativeViewContainerProc(HWND const window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
LRESULT CALLBACK NativeViewContainer::WindowProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY: {
::PostQuitMessage(0);
Expand Down Expand Up @@ -63,49 +107,7 @@ LRESULT CALLBACK NativeViewContainerProc(HWND const window, UINT const message,
return ::DefWindowProc(window, message, wparam, lparam);
}

HWND CreateNativeViewContainer() {
auto window_class = WNDCLASSEX{};
::SecureZeroMemory(&window_class, sizeof(window_class));
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = NativeViewContainerProc;
window_class.hInstance = 0;
window_class.lpszClassName = kClassName;
window_class.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
window_class.hbrBackground = ::CreateSolidBrush(0);
::RegisterClassExW(&window_class);
native_view_container =
::CreateWindow(kClassName, kWindowName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
// Disable DWM animations on |native_view_container|.
auto disable_window_transitions = TRUE;
DwmSetWindowAttribute(native_view_container, DWMWA_TRANSITIONS_FORCEDISABLED,
&disable_window_transitions,
sizeof(disable_window_transitions));
return native_view_container;
}

HWND GetNativeViewContainer(HWND window) {
RECT window_rect;
::GetWindowRect(window, &window_rect);
::SetWindowPos(native_view_container, window, window_rect.left,
window_rect.top, window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top, SWP_NOACTIVATE);
::SetWindowLongPtr(native_view_container, GWLP_USERDATA,
reinterpret_cast<LONG>(window));
// Remove taskbar entry.
// Using |ITaskbarList3| available on Windows 7 or higher. Modification to
// |native_view_container|'s |GWL_STYLE| OR |GWL_EX_STYLE| made underlying
// |HWND| obvious to user.
ITaskbarList3* taskbar = nullptr;
::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&taskbar));
taskbar->DeleteTab(native_view_container);
taskbar->Release();
::ShowWindow(native_view_container, SW_SHOWNOACTIVATE);
::SetFocus(window);
return native_view_container;
}
std::unique_ptr<NativeViewContainer> NativeViewContainer::instance_ =
std::make_unique<NativeViewContainer>();

} // namespace flutternativeview
29 changes: 26 additions & 3 deletions core/native_view_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,32 @@ namespace flutternativeview {
extern "C" {
#endif

DLLEXPORT HWND CreateNativeViewContainer();

DLLEXPORT HWND GetNativeViewContainer(HWND window);
class NativeViewContainer {
public:
DLLEXPORT static NativeViewContainer* GetInstance();

// Creates a new |NativeViewContainer| to hold various native views.
// Transitions on this window are disabled using
// |DWMWA_TRANSITIONS_FORCEDISABLED|.
DLLEXPORT HWND NativeViewContainer::Create();

// Returns the |NativeViewContainer|'s window handle for the given parent
// |window|. The method also removes the taskbar entry of |handle_| & returns
// focus back to |window| (if lost).
// |window| is stored on |NativeViewContainer|'s window as |GWLP_USERDATA|.
DLLEXPORT HWND NativeViewContainer::Get(HWND window);

private:
static std::unique_ptr<NativeViewContainer> NativeViewContainer::instance_;
static constexpr auto kClassName = L"FLUTTER_NATIVE_VIEW";
static constexpr auto kWindowName = L"flutter_native_view";

static LRESULT CALLBACK NativeViewContainer::WindowProc(
HWND const window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept;

HWND handle_;
};

#ifdef __cplusplus
}
Expand Down
19 changes: 11 additions & 8 deletions core/native_view_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ NativeViewCore::NativeViewCore(HWND window, HWND child_window)

void NativeViewCore::EnsureInitialized() {
flutternativeview::SetWindowComposition(window_, 6, 0);
native_view_container_ = flutternativeview::GetNativeViewContainer(window_);
native_view_container_ =
flutternativeview::NativeViewContainer::GetInstance()->Get(window_);
}

void NativeViewCore::CreateNativeView(HWND native_view, RECT rect,
Expand Down Expand Up @@ -101,13 +102,15 @@ std::optional<HRESULT> NativeViewCore::WindowProc(HWND hwnd, UINT message,
case WM_SIZE: {
// Keeping the |native_view_container_| hidden when minimizing the app &
// showing it again only when the app is restored.
if (last_wm_size_wparam_ == SIZE_MINIMIZED) {
std::thread([=]() {
std::this_thread::sleep_for(
std::chrono::milliseconds(kNativeViewPositionAndShowDelay));
::ShowWindow(native_view_container_, SW_SHOWNOACTIVATE);
}).detach();
}
// ---- INTENTIONALLY COMMENTED OUT ----
// if (last_wm_size_wparam_ == SIZE_MINIMIZED) {
// std::thread([=]() {
// std::this_thread::sleep_for(
// std::chrono::milliseconds(kNativeViewPositionAndShowDelay));
// ::ShowWindow(native_view_container_, SW_SHOWNOACTIVATE);
// }).detach();
// }

// Handle Windows's minimize & maximize animations properly.
// Since |SetWindowPos| & other Win32 APIs on |native_view_container_|
// do not re-produce the same DWM animations like actual user
Expand Down
2 changes: 1 addition & 1 deletion example/windows/runner/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
}
window.SetQuitOnClose(true);

flutternativeview::CreateNativeViewContainer();
flutternativeview::NativeViewContainer::GetInstance()->Create();

::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
Expand Down

0 comments on commit 5e6360b

Please sign in to comment.