Files
VibeCoding/webrtc_controller_flutter/windows/runner/flutter_window.cpp

159 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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);
}