159 lines
5.3 KiB
C++
159 lines
5.3 KiB
C++
#include "flutter_window.h"
|
||
|
||
#include <optional>
|
||
#include <variant>
|
||
|
||
#include "flutter/generated_plugin_registrant.h"
|
||
|
||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||
: 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<flutter::FlutterViewController>(
|
||
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());
|
||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||
|
||
// 注册窗口控制通道:Dart 端可请求按视频比例调整窗口尺寸。
|
||
window_channel_ =
|
||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||
flutter_controller_->engine()->messenger(), "app/window",
|
||
&flutter::StandardMethodCodec::GetInstance());
|
||
window_channel_->SetMethodCallHandler(
|
||
[this](const flutter::MethodCall<flutter::EncodableValue>& call,
|
||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
|
||
result) {
|
||
if (call.method_name() == "resizeToVideoAspect") {
|
||
double aspect = 0.0;
|
||
if (const auto* value = std::get_if<double>(call.arguments())) {
|
||
aspect = *value;
|
||
} else if (const auto* ivalue = std::get_if<int>(call.arguments())) {
|
||
aspect = static_cast<double>(*ivalue);
|
||
}
|
||
ResizeToVideoAspect(aspect);
|
||
result->Success();
|
||
} else {
|
||
result->NotImplemented();
|
||
}
|
||
});
|
||
|
||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||
this->Show();
|
||
});
|
||
|
||
// Flutter can complete the first frame before the "show window" callback is
|
||
// registered. The following call ensures a frame is pending to ensure the
|
||
// window is shown. It is a no-op if the first frame hasn't completed yet.
|
||
flutter_controller_->ForceRedraw();
|
||
|
||
return true;
|
||
}
|
||
|
||
void FlutterWindow::OnDestroy() {
|
||
if (flutter_controller_) {
|
||
flutter_controller_ = nullptr;
|
||
}
|
||
|
||
Win32Window::OnDestroy();
|
||
}
|
||
|
||
void FlutterWindow::ResizeToVideoAspect(double aspect) {
|
||
if (aspect <= 0.0) {
|
||
return;
|
||
}
|
||
HWND hwnd = GetHandle();
|
||
if (!hwnd) {
|
||
return;
|
||
}
|
||
|
||
RECT window_rect;
|
||
RECT client_rect;
|
||
GetWindowRect(hwnd, &window_rect);
|
||
GetClientRect(hwnd, &client_rect);
|
||
|
||
const int window_w = window_rect.right - window_rect.left;
|
||
const int window_h = window_rect.bottom - window_rect.top;
|
||
const int client_w = client_rect.right - client_rect.left;
|
||
const int client_h = client_rect.bottom - client_rect.top;
|
||
// 边框(含标题栏)占用的额外像素。
|
||
const int border_w = window_w - client_w;
|
||
const int border_h = window_h - client_h;
|
||
|
||
// 保持窗口宽度不变,按比例计算客户区高度。
|
||
int target_client_w = client_w;
|
||
int target_client_h = static_cast<int>(target_client_w / aspect + 0.5);
|
||
int target_window_w = window_w;
|
||
int target_window_h = target_client_h + border_h;
|
||
|
||
// 获取窗口所在显示器的可用工作区。
|
||
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||
MONITORINFO monitor_info;
|
||
monitor_info.cbSize = sizeof(MONITORINFO);
|
||
GetMonitorInfo(monitor, &monitor_info);
|
||
const int work_h = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
|
||
|
||
// 高度超出屏幕:改为以屏幕高度为准的合适窗口大小。
|
||
if (target_window_h > work_h) {
|
||
target_window_h = work_h;
|
||
target_client_h = target_window_h - border_h;
|
||
target_client_w = static_cast<int>(target_client_h * aspect + 0.5);
|
||
target_window_w = target_client_w + border_w;
|
||
}
|
||
|
||
// 计算位置:尽量保持当前左上角,同时确保窗口落在工作区内。
|
||
int new_left = window_rect.left;
|
||
int new_top = window_rect.top;
|
||
if (new_left + target_window_w > monitor_info.rcWork.right) {
|
||
new_left = monitor_info.rcWork.right - target_window_w;
|
||
}
|
||
if (new_top + target_window_h > monitor_info.rcWork.bottom) {
|
||
new_top = monitor_info.rcWork.bottom - target_window_h;
|
||
}
|
||
if (new_left < monitor_info.rcWork.left) {
|
||
new_left = monitor_info.rcWork.left;
|
||
}
|
||
if (new_top < monitor_info.rcWork.top) {
|
||
new_top = monitor_info.rcWork.top;
|
||
}
|
||
|
||
SetWindowPos(hwnd, nullptr, new_left, new_top, target_window_w,
|
||
target_window_h, SWP_NOZORDER | SWP_NOACTIVATE);
|
||
}
|
||
|
||
LRESULT
|
||
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
|
||
WPARAM const wparam,
|
||
LPARAM const lparam) noexcept {
|
||
// Give Flutter, including plugins, an opportunity to handle window messages.
|
||
if (flutter_controller_) {
|
||
std::optional<LRESULT> 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);
|
||
}
|