Skip to content

Commit

Permalink
optimize the logic of caret position
Browse files Browse the repository at this point in the history
  • Loading branch information
fanlumaster committed Oct 23, 2023
1 parent e74cc01 commit a8bcbe4
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 43 deletions.
Binary file added CHN.ico
Binary file not shown.
3 changes: 2 additions & 1 deletion NotificationIcon.rc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ LANGUAGE 9, 1
// remains consistent on all systems.

IDI_NOTIFICATIONICON ICON "NotificationIcon.ico"
IDI_CHINESEICON ICON "RIME.ico"
// IDI_CHINESEICON ICON "RIME.ico"
IDI_CHINESEICON ICON "CHN.ico"
IDI_ENGLISHICON ICON "ENG.ico"

IDB_PRINTER BITMAP "printer.bmp"
Expand Down
1 change: 1 addition & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "dwmapi.lib")

// 控制 debug 控制台的显隐
// #define FANY_DEBUG

//~ start system tray
Expand Down
Empty file added src/options/flypinyin.cpp
Empty file.
Empty file added src/options/quanpin.cpp
Empty file.
46 changes: 24 additions & 22 deletions src/ui/cand_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
}

void ClearCandUI() {
void ClearCandUI()
{
// 开始绘制
g_pRenderTarget->BeginDraw();
// 清除背景为透明,最后一个通道是 alpha 通道,设置为 0 的时候,就是全透明
Expand All @@ -58,6 +59,28 @@ void ClearCandUI() {
g_pRenderTarget->EndDraw();
}

// 创建资源
void CreateDWResource(HWND hwnd)
{
MARGINS mar = {-1};
DwmExtendFrameIntoClientArea(gHwnd, &mar);
D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &g_pD2DFactory);

g_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)),
D2D1::HwndRenderTargetProperties(hwnd, D2D1::SizeU(306, 406), D2D1_PRESENT_OPTIONS_IMMEDIATELY),
&g_pRenderTarget);
g_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(0.0f, 0.0f, 0.0f), &g_pBrush);
g_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
reinterpret_cast<IUnknown **>(&g_pDWriteFactory));
// std::wstring fontname = L"微软雅黑";
std::wstring fontname = L"思源黑体";
g_pDWriteFactory->CreateTextFormat(fontname.c_str(), NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 17.0f, L"zh-cn", &g_pDWriteTextFormat);
}

// 绘制文本
void FanyDrawText(HWND hwnd, std::wstring wText)
{
Expand Down Expand Up @@ -99,27 +122,6 @@ void FanyDrawText(HWND hwnd, std::wstring wText)
// SAFE_RELEASE(g_pRenderTarget);
}

// 创建资源
void CreateDWResource(HWND hwnd)
{
MARGINS mar = {-1};
DwmExtendFrameIntoClientArea(gHwnd, &mar);
D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &g_pD2DFactory);

g_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)),
D2D1::HwndRenderTargetProperties(hwnd, D2D1::SizeU(306, 406), D2D1_PRESENT_OPTIONS_IMMEDIATELY),
&g_pRenderTarget);
g_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(0.0f, 0.0f, 0.0f), &g_pBrush);
g_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
reinterpret_cast<IUnknown **>(&g_pDWriteFactory));
std::wstring fontname = L"微软雅黑";
g_pDWriteFactory->CreateTextFormat(fontname.c_str(), NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 17.0f, L"zh-cn", &g_pDWriteTextFormat);
}

// 释放资源
void Cleanup()
{
Expand Down
6 changes: 5 additions & 1 deletion src/ui/cand_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// 清空候选框页面的绘制
void ClearCandUI();

void FanyDrawText(HWND hwnd, std::wstring wText);
// 创建 DirectWrite 的资源
void CreateDWResource(HWND hwnd);

// 绘制文本
void FanyDrawText(HWND hwnd, std::wstring wText);

void Cleanup();

// 将拼音字符串格式化,使之带有单引号分隔
Expand Down
84 changes: 65 additions & 19 deletions src/utils/caret_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@
/*
通过 GUIThreadInfo 获取 caret 的坐标
*/
std::pair<int, int> getCaretPosByGUIThreadInfo() {
std::pair<int, int> getCaretPosByGUIThreadInfo()
{
std::pair<int, int> caretPos(0, 0);
HWND target_window = GetForegroundWindow();
GUITHREADINFO info;
info.cbSize = sizeof(GUITHREADINFO);
BOOL result = GetGUIThreadInfo(GetWindowThreadProcessId(target_window, NULL), &info) && info.hwndCaret;
if (!result) {
if (!result)
{
// TODO: error log
}
POINT pt;
pt.x = info.rcCaret.left;
pt.y = info.rcCaret.top;
ClientToScreen(info.hwndCaret, &pt); // 转化成以整个屏幕为坐标系的坐标
if (pt.x == 0 && pt.y == 0) {
ClientToScreen(info.hwndCaret, &pt); // 转化成以整个屏幕为坐标系的坐标
if (pt.x == 0 && pt.y == 0)
{
return caretPos;
}
caretPos.first = pt.x + 8;
Expand All @@ -31,9 +34,11 @@ std::pair<int, int> getCaretPosByGUIThreadInfo() {
/*
通过 Accessible 相关的组件获取
*/
std::pair<int, int> getCaretPosByAcc() {
std::pair<int, int> getCaretPosByAcc()
{
std::pair<int, int> caretPos(0, 0);
typedef struct {
typedef struct
{
long x;
long y;
long w;
Expand All @@ -55,7 +60,8 @@ std::pair<int, int> getCaretPosByAcc() {
GetGUIThreadInfo(tid, &info);

IAccessible *object = nullptr;
if (SUCCEEDED(AccessibleObjectFromWindow(info.hwndFocus, OBJID_CARET, IID_IAccessible, (void **)&object))) {
if (SUCCEEDED(AccessibleObjectFromWindow(info.hwndFocus, OBJID_CARET, IID_IAccessible, (void **)&object)))
{
Rect rect;

VARIANT varCaret;
Expand All @@ -66,11 +72,13 @@ std::pair<int, int> getCaretPosByAcc() {
*/
varCaret.vt = VT_I4;
varCaret.lVal = CHILDID_SELF;
if (SUCCEEDED(object->accLocation(&rect.x, &rect.y, &rect.w, &rect.h, varCaret))) {
if (SUCCEEDED(object->accLocation(&rect.x, &rect.y, &rect.w, &rect.h, varCaret)))
{
/*
加上这个 8 主要是为了解决 VSCode 的光标捕捉在使用 vim 插件的情况下有时会不准确的问题
*/
if (rect.x != 0 && rect.y != 0) {
if (rect.x != 0 && rect.y != 0)
{
caretPos.first = rect.x + 8;
caretPos.second = rect.y + rect.h;
}
Expand All @@ -86,43 +94,81 @@ std::pair<int, int> getCaretPosByAcc() {
/*
通过系统函数获取 cursor 坐标
*/
std::pair<int, int> getCursorPosBySys() {
std::pair<int, int> getCursorPosBySys()
{
std::pair<int, int> cursorPos(0, 0);
POINT cursorPoint;
if (GetCursorPos(&cursorPoint)) {
if (GetCursorPos(&cursorPoint))
{
cursorPos.first = cursorPoint.x;
cursorPos.second = cursorPoint.y;
}
return cursorPos;
}

std::pair<int, int> getCenterPointPosOfCurScreen()
{
// 获取当前活动窗口句柄
HWND activeWindow = GetForegroundWindow();

std::pair<int, int> centerPos;

if (activeWindow)
{
// 获取窗口的边界矩形坐标
RECT windowRect;
GetWindowRect(activeWindow, &windowRect);

// 确定窗口所在的显示器
HMONITOR hMonitor = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONEAREST);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, &monitorInfo);

// 计算显示器的中心坐标
int centerX = monitorInfo.rcMonitor.left + (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left) / 2;
int centerY = monitorInfo.rcMonitor.top + (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top) / 2;

centerPos.first = centerX;
centerPos.second = centerY;
}
return centerPos;
}

/*
整合的、通用的获取 caret 坐标的函数
如果通过前两种方法无法获取到 caret 的坐标,那么,就使用 cursor 坐标来代替
如果通过前两种方法无法获取到 caret 的坐标,那么,就使用 cursor 坐标来代替,
不对,这里是使用当前屏幕的中心点的坐标来替代
TODO: 处理屏幕边缘的相关逻辑
*/
std::pair<int, int> getGeneralCaretPos() {
std::pair<int, int> getGeneralCaretPos()
{
std::pair<int, int> caretPos;
caretPos = getCaretPosByGUIThreadInfo();
if (caretPos.first == 0 && caretPos.second == 0) {
if (caretPos.first == 0 && caretPos.second == 0)
{
caretPos = getCaretPosByAcc();
}
if (caretPos.first == 0 && caretPos.second == 0) {
caretPos = getCursorPosBySys();
if (caretPos.first == 0 && caretPos.second == 0)
{
// caretPos = getCursorPosBySys();
caretPos = getCenterPointPosOfCurScreen();
caretPos.first += 20;
caretPos.second += 30;
}

// std::cout << caretPos.first << '\t' << caretPos.second << '\n';
// 在我这台 pc 上,这里其实可以根据 x 坐标判断是在哪一块屏幕
if (caretPos.first < 3840 && caretPos.second > 1866) {
if (caretPos.first < 3840 && caretPos.second > 1866)
{
caretPos.second -= 35 + (31 * curCandidateVec.size()) + 5 + 35;
}

if (caretPos.first > 3840 && caretPos.second > 1533) {
if (caretPos.first > 3840 && caretPos.second > 1533)
{
caretPos.second -= 35 + (31 * curCandidateVec.size()) + 5 + 35;
}
return caretPos;
}
}
7 changes: 7 additions & 0 deletions src/utils/caret_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@
通过 GUIThreadInfo 获取 caret 的坐标
*/
std::pair<int, int> getCaretPosByGUIThreadInfo();

/*
通过 Accessible 相关的组件获取
*/
std::pair<int, int> getCaretPosByAcc();

/*
通过系统函数获取 cursor 坐标
*/
std::pair<int, int> getCursorPosBySys();

/*
获取当前窗口所在的屏幕的中心点的坐标
*/
std::pair<int, int> getCenterPointPosOfCurScreen();

/*
整合的、通用的获取 caret 坐标的函数
Expand Down

0 comments on commit a8bcbe4

Please sign in to comment.