| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- #include "flutter_window.h"
- #include "utils.h"
- #include "file_info.h"
- #include "file_icon.h"
- #include <optional>
- #include "flutter/generated_plugin_registrant.h"
- void trayMessageHandler(TrayIcon *, UINT);
- HICON getIconHandle(std::string);
- const flutter::EncodableValue* ValueOrNull(const flutter::EncodableMap& map, const char* key);
- static std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> appChannel;
- static std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> trayChannel;
- 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());
- // register channel
- registerMethodChannel(flutter_controller_->engine());
- // setup trayicon
- m_tray.setUserData(this);
- m_tray.setListener(trayMessageHandler);
- 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() {
- m_tray.setVisible(false);
- if (flutter_controller_) {
- flutter_controller_ = nullptr;
- }
- Win32Window::OnDestroy();
- }
- void FlutterWindow::SetMinimumSize(SIZE size) {
- min_window_size_ = size;
- }
- LRESULT
- FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
- WPARAM const wparam,
- LPARAM const lparam) noexcept {
- // my custom message handler
- std::optional<LRESULT> r = handleMessage(hwnd, message, wparam, lparam);
- if (r) {
- return *r;
- }
- // 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;
- case WM_GETMINMAXINFO:
- if (min_window_size_.cx > 0 && min_window_size_.cy > 0) {
- LPMINMAXINFO info = reinterpret_cast<LPMINMAXINFO>(lparam);
- info->ptMinTrackSize.x = min_window_size_.cx;
- info->ptMinTrackSize.y = min_window_size_.cy;
- }
- break;
- }
- return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
- }
- void FlutterWindow::registerMethodChannel(flutter::FlutterEngine* engine)
- {
- appChannel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
- engine->messenger(), "app.nomo/app",
- &flutter::StandardMethodCodec::GetInstance());
- appChannel->SetMethodCallHandler([this](const auto& call, auto result) {
- handleAppMethodCall(call, std::move(result));
- });
- trayChannel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
- engine->messenger(), "app.nomo/tray",
- &flutter::StandardMethodCodec::GetInstance());
- trayChannel->SetMethodCallHandler([this](const auto& call, auto result) {
- handleTrayMethodCall(call, std::move(result));
- });
- }
- void FlutterWindow::handleAppMethodCall(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- if (method_call.method_name().compare("activeWindow") == 0) {
- onActiveWindow();
- result->Success(flutter::EncodableValue(true));
- //
- } else if (method_call.method_name().compare("hideWindow") == 0) {
- onHideWindow();
- result->Success(flutter::EncodableValue(true));
- //
- } else if (method_call.method_name().compare("toggleWindow") == 0) {
- onToggleWindow();
- result->Success(flutter::EncodableValue(true));
- //
- } else if (method_call.method_name().compare("destroyWindow") == 0) {
- Destroy();
- result->Success(flutter::EncodableValue(true));
- //
- } else if (method_call.method_name().compare("extractFileIcon") == 0) {
- extractFileIcon(method_call, std::move(result));
- //
- } else if (method_call.method_name().compare("getFileDescription") == 0) {
- getFileDescription(method_call, std::move(result));
- //
- } else {
- result->NotImplemented();
- }
- }
- void FlutterWindow::handleTrayMethodCall(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- if (method_call.method_name().compare("setSystemTray") == 0) {
- setSystemTray(method_call, std::move(result));
- //
- } else if (method_call.method_name().compare("resetSystemTray") == 0) {
- resetSystemTray(method_call, std::move(result));
- //
- } else if (method_call.method_name().compare("setContextMenu") == 0) {
- setContextMenu(method_call, std::move(result));
- //
- } else if (method_call.method_name().compare("popupContextMenu") == 0) {
- popUpContextMenu(method_call, std::move(result));
- //
- } else {
- result->NotImplemented();
- }
- }
- std::optional<LRESULT> FlutterWindow::handleMessage(HWND window, UINT const message,
- WPARAM const wparam, LPARAM const lparam)
- {
- if (message == WM_CLOSE) {
- if (m_tray.isVisible()) {
- onHideWindow();
- } else {
- Destroy();
- }
- return std::make_optional<LRESULT>(0);
- }
- if (message == WM_COMMAND) {
- flutter::EncodableMap eventData = flutter::EncodableMap();
- eventData[flutter::EncodableValue("id")] = flutter::EncodableValue((int)wparam);
- trayChannel->InvokeMethod("onTrayMenuItemClick",
- std::make_unique<flutter::EncodableValue>(eventData));
- }
- return std::nullopt;
- }
- void FlutterWindow::onActiveWindow()
- {
- bool isVisible = (::GetWindowLong(GetHandle(), GWL_STYLE) & WS_VISIBLE) != 0;
- if (isVisible) {
- WINDOWPLACEMENT windowPlacement;
- GetWindowPlacement(GetHandle(), &windowPlacement);
- if (windowPlacement.showCmd == SW_SHOWMINIMIZED) {
- PostMessage(GetHandle(), WM_SYSCOMMAND, SC_RESTORE, 0);
- }
- } else {
- onShowWindow();
- }
- }
- void FlutterWindow::onToggleWindow()
- {
- bool isVisible = (::GetWindowLong(GetHandle(), GWL_STYLE) & WS_VISIBLE) != 0;
- if (isVisible) {
- WINDOWPLACEMENT windowPlacement;
- GetWindowPlacement(GetHandle(), &windowPlacement);
- if (windowPlacement.showCmd == SW_SHOWMINIMIZED) {
- PostMessage(GetHandle(), WM_SYSCOMMAND, SC_RESTORE, 0);
- } else {
- onHideWindow();
- }
- } else {
- onShowWindow();
- }
- }
- void FlutterWindow::onShowWindow()
- {
- ShowWindow(GetHandle(), SW_SHOW);
- SetForegroundWindow(GetHandle());
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- appChannel->InvokeMethod("onWindowShow", std::move(args));
- }
- void FlutterWindow::onHideWindow()
- {
- ShowWindow(GetHandle(), SW_HIDE);
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- appChannel->InvokeMethod("onWindowHide", std::move(args));
- }
- void FlutterWindow::onTrayIconLButtonDown()
- {
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- trayChannel->InvokeMethod("onTrayIconLButtonDown", std::move(args));
- }
- void FlutterWindow::onTrayIconLButtonUp()
- {
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- trayChannel->InvokeMethod("onTrayIconLButtonUp", std::move(args));
- }
- void FlutterWindow::onTrayIconRButtonDown()
- {
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- trayChannel->InvokeMethod("onTrayIconRButtonDown", std::move(args));
- }
- void FlutterWindow::onTrayIconRButtonUp()
- {
- auto args = std::make_unique<flutter::EncodableValue>(nullptr);
- trayChannel->InvokeMethod("onTrayIconRButtonUp", std::move(args));
- }
- void FlutterWindow::setSystemTray(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- const flutter::EncodableMap& args =
- std::get<flutter::EncodableMap>(*method_call.arguments());
- std::string tooltip = std::get<std::string>(args.at(flutter::EncodableValue("tooltip")));
- std::string icon = std::get<std::string>(args.at(flutter::EncodableValue("icon")));
- m_tray.setName((LPCWSTR)(Utf16FromUtf8(tooltip.c_str()).c_str()));
- m_tray.setIcon(getIconHandle(icon));
- m_tray.setVisible(true);
- result->Success(flutter::EncodableValue(true));
- }
- void FlutterWindow::resetSystemTray(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- m_tray.setVisible(false);
- result->Success(flutter::EncodableValue(true));
- }
- void FlutterWindow::setContextMenu(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- const flutter::EncodableMap& args =
- std::get<flutter::EncodableMap>(*method_call.arguments());
- m_menuHandle = CreatePopupMenu();
- createMenu(m_menuHandle, std::get<flutter::EncodableMap>(
- args.at(flutter::EncodableValue("menu"))));
-
- result->Success(flutter::EncodableValue(true));
- }
- void FlutterWindow::popUpContextMenu(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- POINT pt;
- if (GetCursorPos(&pt)) {
- ::SetForegroundWindow(GetHandle());
- TrackPopupMenu(m_menuHandle, TPM_BOTTOMALIGN | TPM_LEFTALIGN,
- pt.x, pt.y, 0, GetHandle(), NULL);
- }
- result->Success(flutter::EncodableValue(true));
- }
- void FlutterWindow::extractFileIcon(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- const flutter::EncodableMap& args =
- std::get<flutter::EncodableMap>(*method_call.arguments());
- std::string fileName = std::get<std::string>(args.at(flutter::EncodableValue("fileName")));
- std::string iconName = std::get<std::string>(args.at(flutter::EncodableValue("iconName")));
- auto ret = SaveIcon(fileName, iconName, 32, 0);
- result->Success(flutter::EncodableValue(ret));
- }
- void FlutterWindow::getFileDescription(
- const flutter::MethodCall<flutter::EncodableValue>& method_call,
- std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
- {
- const flutter::EncodableMap& args =
- std::get<flutter::EncodableMap>(*method_call.arguments());
- std::wstring fileName = Utf16FromUtf8(std::get<std::string>(args.at(flutter::EncodableValue("fileName"))).c_str());
- std::wstring description;
- FileInfoUtils::GetFileDescription(fileName, description);
- std::wcout << fileName << " " << description << std::endl;
- result->Success(flutter::EncodableValue(utf8_encode(description)));
- }
- void FlutterWindow::createMenu(HMENU menu, flutter::EncodableMap args) {
- flutter::EncodableList items = std::get<flutter::EncodableList>(
- args.at(flutter::EncodableValue("items")));
- int count = GetMenuItemCount(menu);
- for (int i = 0; i < count; i++) {
- // always remove at 0 because they shift every time
- RemoveMenu(menu, 0, MF_BYPOSITION);
- }
- for (flutter::EncodableValue item_value : items) {
- flutter::EncodableMap item_map =
- std::get<flutter::EncodableMap>(item_value);
- int id = std::get<int>(item_map.at(flutter::EncodableValue("id")));
- std::string type =
- std::get<std::string>(item_map.at(flutter::EncodableValue("type")));
- std::string label =
- std::get<std::string>(item_map.at(flutter::EncodableValue("label")));
- auto* checked = std::get_if<bool>(ValueOrNull(item_map, "checked"));
- bool disabled =
- std::get<bool>(item_map.at(flutter::EncodableValue("disabled")));
- UINT_PTR item_id = id;
- UINT uFlags = MF_STRING;
- if (disabled) {
- uFlags |= MF_GRAYED;
- }
- if (type.compare("separator") == 0) {
- AppendMenuW(menu, MF_SEPARATOR, item_id, NULL);
- } else {
- if (type.compare("checkbox") == 0) {
- if (checked == nullptr) {
- // skip
- } else {
- uFlags |= (*checked == true ? MF_CHECKED : MF_UNCHECKED);
- }
- } else if (type.compare("submenu") == 0) {
- uFlags |= MF_POPUP;
- HMENU sub_menu = ::CreatePopupMenu();
- createMenu(sub_menu, std::get<flutter::EncodableMap>(item_map.at(
- flutter::EncodableValue("submenu"))));
- item_id = reinterpret_cast<UINT_PTR>(sub_menu);
- }
- AppendMenuW(menu, uFlags, item_id, (LPCWSTR)(Utf16FromUtf8(label.c_str()).c_str()));
- }
- }
- }
- void trayMessageHandler(TrayIcon *pTrayIcon, UINT uMsg)
- {
- switch (uMsg) {
- case WM_LBUTTONDOWN:
- ((FlutterWindow *)pTrayIcon->getUserData())->onTrayIconLButtonDown();
- break;
- case WM_LBUTTONUP:
- ((FlutterWindow *)pTrayIcon->getUserData())->onTrayIconLButtonUp();
- break;
- case WM_RBUTTONDOWN:
- ((FlutterWindow *)pTrayIcon->getUserData())->onTrayIconRButtonDown();
- break;
- case WM_RBUTTONUP:
- ((FlutterWindow *)pTrayIcon->getUserData())->onTrayIconRButtonUp();
- break;
- }
- }
- HICON getIconHandle(std::string iconPath)
- {
- return static_cast<HICON>(LoadImage(NULL, (LPCWSTR)(Utf16FromUtf8(iconPath.c_str()).c_str()),
- IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_SHARED));
- }
- const flutter::EncodableValue* ValueOrNull(const flutter::EncodableMap& map, const char* key)
- {
- auto it = map.find(flutter::EncodableValue(key));
- if (it == map.end()) {
- return nullptr;
- }
- return &(it->second);
- }
|