Files
msime-windows/server/src/window/ime_windows.cpp
T

941 lines
34 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "global/globals.h"
#include "ipc/ipc.h"
#include "ime_windows.h"
#include "defines/defines.h"
#include "defines/globals.h"
#include <debugapi.h>
#include <minwindef.h>
#include <string>
#include <windef.h>
#include <winuser.h>
#include <fmt/xchar.h>
#include <utf8.h>
#include "webview2/windows_webview2.h"
#include "utils/webview_utils.h"
#include "utils/window_utils.h"
#include <dwmapi.h>
#include "utils/window_utils.h"
#include "ipc/event_listener.h"
#include "utils/ime_utils.h"
#include "window_hook.h"
#include <windowsx.h>
#include "resource/resource.h"
#pragma comment(lib, "dwmapi.lib")
constexpr UINT_PTR TIMER_ID_INIT_WEBVIEW_CAND = 1;
constexpr UINT_PTR TIMER_ID_INIT_WEBVIEW_MENU = 2;
constexpr UINT_PTR TIMER_ID_MOVE_WEBVIEW_SETTINGS = 3;
constexpr UINT_PTR TIMER_ID_MOVE_WEBVIEW_FTB = 4;
constexpr UINT_PTR TIMER_ID_CHECK_TSF_TO_HIDE_FTB = 5;
constexpr UINT_PTR TIMER_ID_PIN_WINDOWS_TO_TOP = 6;
int FineTuneWindow(HWND hwnd);
int FineTuneWindow(HWND hwnd, UINT firstFlag, UINT secondFlag);
LRESULT RegisterCandidateWindowMessage()
{
WM_SHOW_MAIN_WINDOW = RegisterWindowMessage(L"WM_SHOW_MAIN_WINDOW");
WM_HIDE_MAIN_WINDOW = RegisterWindowMessage(L"WM_HIDE_MAIN_WINDOW");
WM_MOVE_CANDIDATE_WINDOW = RegisterWindowMessage(L"WM_MOVE_CANDIDATE_WINDOW");
return 0;
}
LRESULT RegisterIMEWindowsClass(WNDCLASSEX &wcex, HINSTANCE hInstance)
{
//
// 注册窗口类
//
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_IME_ICON));
wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_IME_ICON));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
/* We do not need background color, otherwise it will flash when rendering */
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
OutputDebugString(fmt::format(L"Call to RegisterClassEx failed!\n").c_str());
return 1;
}
return 0;
}
int CreateCandidateWindow(HINSTANCE hInstance)
{
//
// 候选框窗口
//
DWORD dwExStyle = WS_EX_LAYERED | //
WS_EX_TOOLWINDOW | //
WS_EX_NOACTIVATE; //
// WS_EX_TOPMOST; //
FLOAT scale = GetForegroundWindowScale();
HWND hwnd_cand = CreateWindowEx( //
dwExStyle, //
szWindowClass, //
lpWindowNameCand, //
WS_POPUP, //
100, //
100, //
(::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale, //
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale, //
nullptr, //
nullptr, //
hInstance, //
nullptr //
); //
if (!hwnd_cand)
{
OutputDebugString(fmt::format(L"Call to CreateWindow for candidate window failed!\n").c_str());
return 1;
}
else
{
// Set the window to be fully not transparent
SetLayeredWindowAttributes(hwnd_cand, 0, 255, LWA_ALPHA);
MARGINS mar = {-1};
DwmExtendFrameIntoClientArea(hwnd_cand, &mar);
}
::global_hwnd = hwnd_cand;
SetWindowPos( //
hwnd_cand, //
HWND_TOP, //
-10000, //
-10000, //
(::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale, //
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale, //
SWP_SHOWWINDOW //
);
SetWindowPos( //
hwnd_cand, //
HWND_TOP, //
100, //
100, //
(::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale, //
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale, //
SWP_SHOWWINDOW //
);
//
// 任务栏托盘区的菜单窗口
// TODO: 这里的初始 width 和 height 需要设置足够大,不然,底部的 item 会不接受响应。不然,也可以在 wndProc
// 中刷新一下 webview
//
dwExStyle = WS_EX_LAYERED | //
WS_EX_TOOLWINDOW | //
WS_EX_NOACTIVATE; //
// WS_EX_TOPMOST; //
HWND hwnd_menu = CreateWindowEx( //
dwExStyle, //
szWindowClass, //
lpWindowNameMenu, //
WS_POPUP, //
200, //
200, //
(::MENU_WINDOW_WIDTH)*scale, //
(::MENU_WINDOW_HEIGHT * 2) * scale, //
nullptr, //
nullptr, //
hInstance, //
nullptr //
); //
if (!hwnd_menu)
{
OutputDebugString(fmt::format(L"Call to CreateWindow for menu failed!\n").c_str());
return 1;
}
::global_hwnd_menu = hwnd_menu;
//
// settings 窗口
//
dwExStyle = 0; //
HWND hwnd_settings = CreateWindowEx( //
dwExStyle, //
szWindowClass, //
lpWindowNameSettings, //
WS_OVERLAPPEDWINDOW, //
600, // Initial position
-20000, //
(::SETTINGS_WINDOW_WIDTH)*scale, //
(::SETTINGS_WINDOW_HEIGHT)*scale, //
nullptr, //
nullptr, //
hInstance, //
nullptr //
); //
if (!hwnd_settings)
{
OutputDebugString(fmt::format(L"Call to CreateWindow for settings failed!\n").c_str());
return 1;
}
// 使用 DWM 允许透明
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = TRUE;
bb.hRgnBlur = nullptr;
DwmEnableBlurBehindWindow(hwnd_settings, &bb);
BOOL useDarkMode = TRUE;
DwmSetWindowAttribute( //
hwnd_settings, //
DWMWA_USE_IMMERSIVE_DARK_MODE, //
&useDarkMode, //
sizeof(useDarkMode) //
);
::global_hwnd_settings = hwnd_settings;
// 设置 settings 窗口的自定义图标
HICON hSettingsIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SETTINGS_ICON));
HICON hIMEIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_IME_ICON));
SendMessage(hwnd_settings, WM_SETICON, ICON_SMALL, (LPARAM)hSettingsIcon);
SendMessage(hwnd_settings, WM_SETICON, ICON_BIG, (LPARAM)hIMEIcon);
BOOL cloak = TRUE;
DwmSetWindowAttribute(hwnd_settings, DWMWA_CLOAK, &cloak, sizeof(cloak));
/* 设置标题栏和窗口区域为 mica 材质 */
MARGINS margins = {-1};
DwmExtendFrameIntoClientArea(hwnd_settings, &margins);
DWM_SYSTEMBACKDROP_TYPE backdrop_settings = DWMSBT_MAINWINDOW; // Mica 背景类型
DwmSetWindowAttribute(hwnd_settings, DWMWA_SYSTEMBACKDROP_TYPE, &backdrop_settings, sizeof(backdrop_settings));
//
// floating toolbar 窗口
//
dwExStyle = WS_EX_LAYERED | //
WS_EX_TOOLWINDOW | //
WS_EX_NOACTIVATE; //
// WS_EX_TOPMOST; //
HWND hwnd_ftb = CreateWindowEx( //
dwExStyle, //
szWindowClass, //
lpWindowNameFtb, //
WS_POPUP, //
2216, //
-12540, //
(::FTB_WND_WIDTH + ::FTB_WND_SHADOW_WIDTH) * scale, //
(::FTB_WND_HEIGHT + ::FTB_WND_SHADOW_WIDTH) * scale, //
nullptr, //
nullptr, //
hInstance, //
nullptr //
); //
if (!hwnd_ftb)
{
OutputDebugString(fmt::format(L"Call to CreateWindow for floating toolbar failed!\n").c_str());
return 1;
}
::global_hwnd_ftb = hwnd_ftb;
//
// 候选窗口、菜单窗口、settings 窗口、floating toolbar 窗口、floating toolbar hover tip 窗口
//
ShowWindow(hwnd_cand, SW_SHOW);
ShowWindow(hwnd_menu, SW_SHOW);
ShowWindow(hwnd_settings, SW_SHOWMINIMIZED);
ShowWindow(hwnd_settings, SW_RESTORE);
ShowWindow(hwnd_ftb, SW_SHOW);
UpdateWindow(hwnd_cand);
UpdateWindow(hwnd_menu);
UpdateWindow(hwnd_settings);
UpdateWindow(hwnd_ftb);
//
// Preparing webview2 env
//
PrepareHtmlForWnds();
/* 候选框窗口 */
InitWebviewCandWnd(hwnd_cand);
/* 托盘语言区右键菜单窗口 */
InitWebviewMenuWnd(hwnd_menu);
/* settings 窗口 */
InitWebviewSettingsWnd(hwnd_settings);
/* flaoting toolbar 窗口 */
InitWebviewFtbWnd(hwnd_ftb);
/* 调整候选框窗口 size,顺便置顶 */
SetTimer(hwnd_cand, TIMER_ID_INIT_WEBVIEW_CAND, 200, nullptr);
/* 调整菜单窗口 size,顺便置顶 */
SetTimer(hwnd_menu, TIMER_ID_INIT_WEBVIEW_MENU, 200, nullptr);
/* 调整 settings 窗口 position,顺便置顶 */
SetTimer(hwnd_settings, TIMER_ID_MOVE_WEBVIEW_SETTINGS, 200, nullptr);
/* 调整 floating toolbar 窗口 position,顺便置顶 */
SetTimer(hwnd_ftb, TIMER_ID_MOVE_WEBVIEW_FTB, 200, nullptr);
//
// 注册一下全局钩子
//
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
if (!g_hHook)
{
OutputDebugString(fmt::format(L"Kbd hook for IME failed!\n").c_str());
return 1;
}
OutputDebugString(fmt::format(L"Kbd hook for IME installed.\n").c_str());
HWINEVENTHOOK hook = SetWinEventHook( //
EVENT_SYSTEM_FOREGROUND, //
EVENT_OBJECT_LOCATIONCHANGE, //
nullptr, //
WinEventProc, //
0, //
0, //
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* 卸载钩子 */
UnhookWindowsHookEx(g_hHook);
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* 候选窗口 */
if (hwnd == ::global_hwnd)
{
return WndProcCandWindow(hwnd, message, wParam, lParam);
}
/* tray icon 菜单窗口 */
if (hwnd == ::global_hwnd_menu)
{
return WndProcMenuWindow(hwnd, message, wParam, lParam);
}
/* settings 窗口 */
if (hwnd == ::global_hwnd_settings)
{
return WndProcSettingsWindow(hwnd, message, wParam, lParam);
}
/* floating toolbar 窗口 */
if (hwnd == ::global_hwnd_ftb)
{
return WndProcFtbWindow(hwnd, message, wParam, lParam);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_SHOW_MAIN_WINDOW)
{
/* Read candidate string */
::ReadDataFromSharedMemory(0b1000000);
std::wstring preedit = GetPreedit();
std::wstring str = preedit + L"," + Global::CandidateString;
InflateMeasureDivCandWnd(str);
FineTuneWindow(hwnd);
::is_global_wnd_cand_shown = true;
return 0;
}
if (message == WM_HIDE_MAIN_WINDOW)
{
FLOAT scale = GetForegroundWindowScale();
if (scale < 1.5)
{
scale = 1.5;
}
SetWindowPos( //
hwnd, //
HWND_TOP, //
0, //
Global::INVALID_Y, //
(::CANDIDATE_WINDOW_WIDTH + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale, //
(::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale, //
SWP_SHOWWINDOW //
);
UpdateHtmlContentWithJavaScript(webviewCandWnd, L"");
/* 候选词部分使用全角空格来占位 */
// std::wstring str = L" , , , , , , , , ";
// InflateCandWnd(str);
::is_global_wnd_cand_shown = false;
return 0;
}
if (message == WM_MOVE_CANDIDATE_WINDOW)
{
FineTuneWindow(hwnd);
return 0;
}
if (message == WM_IMEACTIVATE)
{
ShowWindow(::global_hwnd_ftb, SW_SHOW);
return 0;
}
if (message == WM_IMEDEACTIVATE)
{
ShowWindow(::global_hwnd_ftb, SW_HIDE);
// 设置一个定时任务,每个 200ms,用来检测新的窗口焦点是否有 tsf 命名管道连接上,如果没有,再去隐藏窗口
SetTimer(::global_hwnd_ftb, TIMER_ID_CHECK_TSF_TO_HIDE_FTB, 200, nullptr);
return 0;
}
if (message == WM_IMESWITCH)
{
if (wParam == 0) // 此时是英文状态
{
/* 更新 floating toolbar 的中英文状态为英文,同时更新标点的全角和半角状态 */
UpdateFtbCnEnAndPuncState(::webviewFtbWnd, 0, 0);
}
else // 此时是中文状态
{
/* 更新 floating toolbar 的中英文状态为中文,同时更新标点的全角和半角状态 */
UpdateFtbCnEnAndPuncState(::webviewFtbWnd, 1, 1);
}
return 0;
}
if (message == WM_PUNCSWITCH)
{
if (wParam == 0) // 此时是中文标点状态
{
/* 更新 floating toolbar 的标点全角和半角状态为全角 */
UpdateFtbPuncState(::webviewFtbWnd, 0);
}
else // 此时是英文标点状态
{
/* 更新 floating toolbar 的标点全角和半角状态为半角 */
UpdateFtbPuncState(::webviewFtbWnd, 1);
}
return 0;
}
if (message == WM_DOUBLESINGLEBYTESWITCH)
{
if (wParam == 0) // 此时是半角状态
{
/* 更新 floating toolbar 的全角和半角状态为半角 */
UpdateFtbDoubleSingleByteState(::webviewFtbWnd, 0);
}
else // 此时是全角状态
{
/* 更新 floating toolbar 的全角和半角状态为全角 */
UpdateFtbDoubleSingleByteState(::webviewFtbWnd, 1);
}
return 0;
}
switch (message)
{
case WM_TIMER: {
if (wParam == TIMER_ID_PIN_WINDOWS_TO_TOP)
{
KillTimer(hwnd, TIMER_ID_PIN_WINDOWS_TO_TOP);
if (::global_hwnd)
{
SetWindowPos( //
::global_hwnd, //
HWND_TOPMOST, //
0, //
0, //
0, //
0, //
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE //
);
}
if (::global_hwnd_ftb)
{
SetWindowPos( //
::global_hwnd_ftb, //
HWND_TOPMOST, //
0, //
0, //
0, //
0, //
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE //
);
}
if (::global_hwnd_menu)
{
SetWindowPos( //
::global_hwnd_menu, //
HWND_TOPMOST, //
0, //
0, //
0, //
0, //
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE //
);
}
}
if (wParam == TIMER_ID_INIT_WEBVIEW_CAND)
{
KillTimer(hwnd, TIMER_ID_INIT_WEBVIEW_CAND);
if (::webviewCandWnd)
{
UINT flag = SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE;
SetWindowPos( //
::global_hwnd, //
HWND_TOPMOST, //
0, //
0, //
0, //
0, //
flag);
break;
}
else
{
SetTimer(hwnd, TIMER_ID_INIT_WEBVIEW_CAND, 100, nullptr);
break;
}
}
break;
}
case WM_MOUSEACTIVATE:
// Stop the window from being activated by mouse click
return MA_NOACTIVATE;
case WM_ACTIVATE: {
if (LOWORD(wParam) != WA_INACTIVE)
{
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
}
break;
}
/* Clear dictionary buffer cache */
case WM_CLS_DICT_CACHE: {
g_dictQuery->reset_cache();
OutputDebugString(fmt::format(L"Cleared dictionary buffer cache.\n").c_str());
break;
}
case WM_COMMIT_CANDIDATE: {
int one_based = static_cast<int>(wParam);
int zero_based = one_based - 1;
OutputDebugString(fmt::format(L"Really to commit candidate {}\n", one_based).c_str());
if (one_based > Global::CandidateWordList.size())
{
break;
}
/* 取出汉字 */
UINT keycode = '0' + one_based;
FanyNamedPipe::ProcessSelectionKey(keycode);
/* 触发事件,将候选词数据写入管道 */
HANDLE hEventWritePipe = OpenEvent( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_PIPE_ARRAY[0].c_str() //
); //
if (hEventWritePipe)
{
SetEvent(hEventWritePipe);
CloseHandle(hEventWritePipe);
}
else
{
OutputDebugString(L"Failed to open event for writing candidate word\n");
}
/* 触发事件,发送消息到 tsf worker thread */
HANDLE hEventSendMsgToTsfWorkerThread = OpenEvent( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_PIPE_TO_TSF_WORKER_THREAD_ARRAY[7].c_str() // CommitCandidate
); //
if (hEventSendMsgToTsfWorkerThread)
{
SetEvent(hEventSendMsgToTsfWorkerThread);
CloseHandle(hEventSendMsgToTsfWorkerThread);
}
else
{
OutputDebugString(L"Failed to open event for sending message to tsf worker thread\n");
}
break;
}
case WM_PIN_TO_TOP_CANDIDATE: {
int one_based = static_cast<int>(wParam);
int zero_based = one_based - 1;
OutputDebugString(fmt::format(L"Really to pin to top candidate {}\n", one_based).c_str());
if (one_based > Global::CandidateWordList.size())
{
break;
}
//
// 在词库中调整 weight 以置顶
//
/* 先取出拼音和汉字 */
DictionaryUlPb::WordItem curWordItem =
Global::CandidateList[zero_based + Global::PageIndex * Global::CountOfOnePage];
std::string curWord = std::get<1>(curWordItem);
std::string curWordPinyin = std::get<0>(curWordItem);
/* 调整条目 weight,一次到顶 */
g_dictQuery->update_weight_by_pinyin_and_word(curWordPinyin, curWord);
/* 刷新候选窗列表 */
g_dictQuery->reset_cache();
g_dictQuery->handleVkCode(0, 0); // 重新查一次
/* 刷新窗口 */
FanyNamedPipe::PrepareCandidateList();
PostMessage(hwnd, WM_SHOW_MAIN_WINDOW, 0, 0);
break;
}
case WM_DELETE_CANDIDATE: {
int one_based = static_cast<int>(wParam);
int zero_based = one_based - 1;
OutputDebugString(fmt::format(L"Really to delete candidate {}\n", one_based).c_str());
if (one_based > Global::CandidateWordList.size())
{
break;
}
//
// 在词库中删除
//
/* 先取出拼音和汉字 */
DictionaryUlPb::WordItem curWordItem =
Global::CandidateList[zero_based + Global::PageIndex * Global::CountOfOnePage];
std::string curWord = std::get<1>(curWordItem);
std::string curWordPinyin = std::get<0>(curWordItem);
// 单字不删除,静默无任何操作来处理
if (utf8::distance(curWord.begin(), curWord.end()) == 1)
{
break;
}
/* 删除条目 */
g_dictQuery->delete_by_pinyin_and_word(curWordPinyin, curWord);
/* 刷新候选窗列表 */
g_dictQuery->reset_cache();
g_dictQuery->handleVkCode(0, 0); // 重新查一次
/* 刷新窗口 */
FanyNamedPipe::PrepareCandidateList();
PostMessage(hwnd, WM_SHOW_MAIN_WINDOW, 0, 0);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProcMenuWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LANGBAR_RIGHTCLICK: {
int left = Global::Point[0];
int top = Global::Point[1];
int right = Global::Keycode;
int bottom = Global::ModifiersDown;
int iconWidth = (right - left) * ::SCALE;
int iconHeight = (bottom - top) * ::SCALE;
int iconMiddleX = left + iconWidth / 2;
int menuX = iconMiddleX - ::MENU_WINDOW_WIDTH / 2;
int menuY = top - ::MENU_WINDOW_HEIGHT;
UINT flag = SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER;
SetLastError(0);
BOOL okShowMenu = SetWindowPos( //
::global_hwnd_menu, //
HWND_TOPMOST, //
menuX, //
menuY, //
0, //
0, //
flag //
);
SetForegroundWindow(::global_hwnd_menu);
/* 安装鼠标钩子 */
g_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, nullptr, 0);
break;
}
case WM_TIMER: {
if (wParam == TIMER_ID_INIT_WEBVIEW_MENU)
{
KillTimer(hwnd, TIMER_ID_INIT_WEBVIEW_MENU);
if (::webviewMenuWnd) // 确保 webview 已初始化
{
GetContainerSizeMenu(webviewMenuWnd, [hwnd](std::pair<double, double> containerSize) {
if (hwnd == ::global_hwnd_menu)
{
// UINT flag = SWP_NOZORDER | SWP_NOMOVE | SWP_HIDEWINDOW;
UINT flag = SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE;
FLOAT scale = GetForegroundWindowScale();
int newWidth = (containerSize.first) * scale;
int newHeight = (containerSize.second) * scale;
::SCALE = scale;
::MENU_WINDOW_WIDTH = newWidth;
::MENU_WINDOW_HEIGHT = newHeight;
/* 调整菜单窗口 size */
SetWindowPos( //
hwnd, //
HWND_TOPMOST, //
0, //
0, //
newWidth, //
newHeight, //
flag //
);
}
});
}
else
{
// 如果 webview 还没准备好,再等一会
SetTimer(hwnd, TIMER_ID_INIT_WEBVIEW_MENU, 100, nullptr);
}
}
break;
}
default: {
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
return 0;
}
LRESULT CALLBACK WndProcSettingsWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER: {
if (wParam == TIMER_ID_MOVE_WEBVIEW_SETTINGS)
{
KillTimer(hwnd, TIMER_ID_MOVE_WEBVIEW_SETTINGS);
if (::webviewSettingsWnd)
{
// 放在屏幕右下角
// 获取主屏幕尺寸
MonitorCoordinates coordinates = GetMainMonitorCoordinates();
// 获取窗口尺寸
RECT rect;
GetWindowRect(hwnd, &rect);
// 获取任务栏高度
int taskbarHeight = GetTaskbarHeight();
// 移动窗口
SetWindowPos( //
hwnd, //
0, //
coordinates.right / 2 - (rect.right - rect.left) / 2, //
coordinates.bottom / 2 - (rect.bottom - rect.top) / 2 - taskbarHeight, //
0, //
0, //
SWP_NOSIZE | SWP_HIDEWINDOW);
break;
}
else
{
// 如果 webview 还没准备好,再等一会
SetTimer(hwnd, TIMER_ID_MOVE_WEBVIEW_SETTINGS, 100, nullptr);
}
}
break;
}
case WM_CLOSE: {
// 不销毁窗口,只隐藏
ShowWindow(hwnd, SW_HIDE);
return 0;
}
case WM_ERASEBKGND: { // Make the background dark
// HDC hdc = (HDC)wParam;
// RECT rc;
// GetClientRect(hwnd, &rc);
// HBRUSH darkBrush = CreateSolidBrush(RGB(32, 32, 32));
// FillRect(hdc, &rc, darkBrush);
// DeleteObject(darkBrush);
return 1;
}
case WM_SIZE: {
if (::webviewControllerSettingsWnd)
{
RECT rect;
GetClientRect(hwnd, &rect);
webviewControllerSettingsWnd->put_Bounds(rect);
}
break;
}
default: {
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
return 0;
}
LRESULT CALLBACK WndProcFtbWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int s_check_cnt = 0;
switch (message)
{
case UPDATE_FTB_STATUS: {
int cnEnState = (wParam >> 2) & 0x1;
int doubleSingleByteState = (wParam >> 1) & 0x1;
int puncState = wParam & 0x1;
UpdateFtbCnEnAndDoubleSingleAndPuncState(::webviewFtbWnd, cnEnState, doubleSingleByteState, puncState);
break;
}
case WM_TIMER: {
if (wParam == TIMER_ID_MOVE_WEBVIEW_FTB)
{
KillTimer(hwnd, TIMER_ID_MOVE_WEBVIEW_FTB);
if (::webviewFtbWnd)
{
// 放在屏幕右下角
// 获取主屏幕尺寸
MonitorCoordinates coordinates = GetMainMonitorCoordinates();
// 获取窗口尺寸
RECT rect;
GetWindowRect(hwnd, &rect);
// 获取任务栏高度
int taskbarHeight = GetTaskbarHeight();
// 移动窗口
SetWindowPos( //
hwnd, //
HWND_TOPMOST, //
coordinates.right - (rect.right - rect.left) - 10, //
coordinates.bottom - (rect.bottom - rect.top) - taskbarHeight - 10, //
0, //
0, //
SWP_NOSIZE);
break;
}
else
{
// 如果 webview 还没准备好,再等一会
SetTimer(hwnd, TIMER_ID_MOVE_WEBVIEW_FTB, 100, nullptr);
}
}
else if (wParam == TIMER_ID_CHECK_TSF_TO_HIDE_FTB)
{
KillTimer(hwnd, TIMER_ID_CHECK_TSF_TO_HIDE_FTB);
s_check_cnt++;
// TODO: 需要更稳定的写法。
/* 需要检查两次,第二次的时候再确定 */
if (s_check_cnt < 2)
{
SetTimer(hwnd, TIMER_ID_CHECK_TSF_TO_HIDE_FTB, 200, nullptr);
}
else
{
if (::toTsfWorkerThreadConnected)
{
SetWindowPos( //
hwnd, //
HWND_TOPMOST, //
0, //
0, //
0, //
0, //
SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE);
}
s_check_cnt = 0;
}
}
break;
}
default: {
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
return 0;
}
int FineTuneWindow(HWND hwnd)
{
UINT flag = SWP_NOZORDER | SWP_SHOWWINDOW;
FLOAT scale = GetForegroundWindowScale();
int caretX = Global::Point[0];
int caretY = Global::Point[1];
std::shared_ptr<std::pair<int, int>> properPos = std::make_shared<std::pair<int, int>>();
GetContainerSizeCand(webviewCandWnd, [flag, //
scale, //
caretX, //
caretY, //
properPos, //
hwnd](std::pair<double, double> containerSize) {
POINT pt = {caretX, caretY};
/* Whether need to adjust candidate window position */
if (caretY == Global::INVALID_Y)
{
properPos->first = caretX;
properPos->second = caretY;
}
else
{
AdjustCandidateWindowPosition(&pt, containerSize, properPos);
}
std::wstring preedit = GetPreedit();
std::wstring str = preedit + L"," + Global::CandidateString;
InflateCandWnd(str);
int newWidth = 0;
int newHeight = 0;
UINT newFlag = flag;
/* 默认情况下,输入法候选框是不用动的 */
if (containerSize.first > ::CANDIDATE_WINDOW_WIDTH)
{
newWidth = (containerSize.first + ::SHADOW_WIDTH + ::POP_UP_WND_WIDTH) * scale;
newHeight = (::CANDIDATE_WINDOW_HEIGHT + ::SHADOW_HEIGHT + ::POP_UP_WND_HEIGHT) * scale;
// newHeight = (containerSize.second + ::SHADOW_WIDTH) * scale;
}
else
{
newFlag = flag | SWP_NOSIZE;
}
SetWindowPos( //
hwnd, //
nullptr, //
properPos->first, //
properPos->second, //
newWidth, //
newHeight, //
newFlag //
);
});
return 0;
}