aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2025-04-23 10:10:40 +1000
committerNeil <nyamatongwe@gmail.com>2025-04-23 10:10:40 +1000
commit3c61f77965c6e4dd59b3a704cc62f43fb118f5d7 (patch)
tree794889acaa3ae5e8060d4c556d21fa17d0fa56f0
parent7b58306ced21fc17a6c27931626552f70ea12ceb (diff)
downloadscintilla-mirror-3c61f77965c6e4dd59b3a704cc62f43fb118f5d7.tar.gz
Encapsulate IME input context fully in IMContext class. All access to HIMC goes
through this now.
-rw-r--r--win32/ScintillaWin.cxx157
1 files changed, 86 insertions, 71 deletions
diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx
index c05dbd8fc..3975846f0 100644
--- a/win32/ScintillaWin.cxx
+++ b/win32/ScintillaWin.cxx
@@ -261,23 +261,30 @@ public:
class IMContext {
HWND hwnd;
-public:
HIMC hIMC;
+public:
+
explicit IMContext(HWND hwnd_) noexcept :
hwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {
}
+
// Deleted so IMContext objects can not be copied.
IMContext(const IMContext &) = delete;
IMContext(IMContext &&) = delete;
IMContext &operator=(const IMContext &) = delete;
IMContext &operator=(IMContext &&) = delete;
+
~IMContext() {
if (hIMC)
::ImmReleaseContext(hwnd, hIMC);
}
+ [[nodiscard]] explicit operator bool() const noexcept {
+ return hIMC != HIMC{};
+ }
+
[[nodiscard]] unsigned int GetImeCaretPos() const noexcept {
- return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
+ return ::ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
}
[[nodiscard]] std::vector<BYTE> GetImeAttributes() const {
@@ -298,6 +305,59 @@ public:
::ImmGetCompositionStringW(hIMC, dwIndex, wcs.data(), byteLen);
return wcs;
}
+
+ BOOL SetCompositionString(DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen) const noexcept {
+ return ::ImmSetCompositionStringW(hIMC, dwIndex, lpComp, dwCompLen, nullptr, 0);
+ }
+
+ void SetCompositionWindow(Point pos) const noexcept {
+ COMPOSITIONFORM CompForm{};
+ CompForm.dwStyle = CFS_POINT;
+ CompForm.ptCurrentPos = POINTFromPoint(pos);
+ ::ImmSetCompositionWindow(hIMC, &CompForm);
+ }
+
+ void SetCandidateWindowPos(Point pos, PRectangle rcClient, int lineHeight) const noexcept {
+ CANDIDATEFORM CandForm{};
+ CandForm.dwIndex = 0;
+ CandForm.dwStyle = CFS_EXCLUDE;
+ CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
+ CandForm.ptCurrentPos.y = static_cast<int>(pos.y + std::max(4, lineHeight / 4));
+ // Exclude the area of the whole caret line
+ CandForm.rcArea.top = static_cast<int>(pos.y);
+ CandForm.rcArea.bottom = static_cast<int>(pos.y + lineHeight);
+ CandForm.rcArea.left = static_cast<int>(rcClient.left);
+ CandForm.rcArea.right = static_cast<int>(rcClient.right);
+ ::ImmSetCandidateWindow(hIMC, &CandForm);
+ }
+
+ void SetCompositionFont(const ViewStyle &vs, int style, UINT dpi) const {
+ LOGFONTW lf{};
+ int sizeZoomed = vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier);
+ if (sizeZoomed <= 2 * FontSizeMultiplier) // Hangs if sizeZoomed <= 1
+ sizeZoomed = 2 * FontSizeMultiplier;
+ // The negative is to allow for leading
+ lf.lfHeight = -::MulDiv(sizeZoomed, dpi, pointsPerInch * FontSizeMultiplier);
+ lf.lfWeight = static_cast<LONG>(vs.styles[style].weight);
+ lf.lfItalic = vs.styles[style].italic ? 1 : 0;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfFaceName[0] = L'\0';
+ if (vs.styles[style].fontName) {
+ const char *fontName = vs.styles[style].fontName;
+ UTF16FromUTF8(std::string_view(fontName), lf.lfFaceName, LF_FACESIZE);
+ }
+
+ ::ImmSetCompositionFontW(hIMC, &lf);
+ }
+
+ void Notify(bool complete) const noexcept {
+ ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, complete ? CPS_COMPLETE : CPS_CANCEL, 0);
+ }
+
+ LRESULT Escape(HKL hkl, UINT uEscape, LPVOID lpv) const noexcept {
+ return ::ImmEscapeW(hkl, hIMC, uEscape, lpv);
+ }
+
};
class GlobalMemory;
@@ -1306,16 +1366,11 @@ sptr_t ScintillaWin::WndPaint() {
sptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {
if (lParam & GCS_RESULTSTR) {
- IMContext imc(MainHWND());
- if (imc.hIMC) {
+ if (IMContext imc{ MainHWND() }) {
AddWString(imc.GetCompositionString(GCS_RESULTSTR), CharacterSource::ImeResult);
// Set new position after converted
- const Point pos = PointMainCaret();
- COMPOSITIONFORM CompForm {};
- CompForm.dwStyle = CFS_POINT;
- CompForm.ptCurrentPos = POINTFromPoint(pos);
- ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
+ imc.SetCompositionWindow(PointMainCaret());
}
return 0;
}
@@ -1328,21 +1383,8 @@ bool ScintillaWin::KoreanIME() noexcept {
}
void ScintillaWin::SetCandidateWindowPos() {
- IMContext imc(MainHWND());
- if (imc.hIMC) {
- const Point pos = PointMainCaret();
- const PRectangle rcClient = GetTextRectangle();
- CANDIDATEFORM CandForm{};
- CandForm.dwIndex = 0;
- CandForm.dwStyle = CFS_EXCLUDE;
- CandForm.ptCurrentPos.x = static_cast<int>(pos.x);
- CandForm.ptCurrentPos.y = static_cast<int>(pos.y + std::max(4, vs.lineHeight/4));
- // Exclude the area of the whole caret line
- CandForm.rcArea.top = static_cast<int>(pos.y);
- CandForm.rcArea.bottom = static_cast<int>(pos.y + vs.lineHeight);
- CandForm.rcArea.left = static_cast<int>(rcClient.left);
- CandForm.rcArea.right = static_cast<int>(rcClient.right);
- ::ImmSetCandidateWindow(imc.hIMC, &CandForm);
+ if (IMContext imc{ MainHWND() }) {
+ imc.SetCandidateWindowPos(PointMainCaret(), GetTextRectangle(), vs.lineHeight);
}
}
@@ -1391,12 +1433,11 @@ void ScintillaWin::EscapeHanja() {
std::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());
- IMContext imc(MainHWND());
- if (imc.hIMC) {
+ if (IMContext imc{ MainHWND() }) {
// Set the candidate box position since IME may show it.
SetCandidateWindowPos();
// IME_ESC_HANJA_MODE appears to receive the first character only.
- if (::ImmEscapeW(GetKeyboardLayout(0), imc.hIMC, IME_ESC_HANJA_MODE, uniChar.data())) {
+ if (imc.Escape(GetKeyboardLayout(0), IME_ESC_HANJA_MODE, uniChar.data())) {
SetSelection(currentPos, currentPos + oneCharLen);
}
}
@@ -1461,10 +1502,10 @@ sptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {
// Great thanks for my foreruners, jiniya and BLUEnLIVE.
IMContext imc(MainHWND());
- if (!imc.hIMC)
+ if (!imc)
return 0;
if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
- ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+ imc.Notify(false);
return 0;
}
@@ -1790,21 +1831,14 @@ void ScintillaWin::SizeWindow() {
sptr_t ScintillaWin::MouseMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
switch (iMessage) {
- case WM_LBUTTONDOWN: {
- // For IME, set the composition string as the result string.
- IMContext imc(MainHWND());
- if (imc.hIMC) {
- ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
- }
- //
- //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam,
- // KeyboardIsKeyDown(VK_SHIFT),
- // KeyboardIsKeyDown(VK_CONTROL),
- // KeyboardIsKeyDown(VK_MENU));
- ::SetFocus(MainHWND());
- ButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),
- MouseModifiers(wParam));
+ case WM_LBUTTONDOWN:
+ // For IME, set the composition string as the result string.
+ if (IMContext imc{ MainHWND() }) {
+ imc.Notify(true);
}
+ ::SetFocus(MainHWND());
+ ButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),
+ MouseModifiers(wParam));
break;
case WM_LBUTTONUP:
@@ -1989,9 +2023,8 @@ sptr_t ScintillaWin::FocusMessage(unsigned int iMessage, uptr_t wParam, sptr_t)
DestroySystemCaret();
}
// Explicitly complete any IME composition
- IMContext imc(MainHWND());
- if (imc.hIMC) {
- ::ImmNotifyIME(imc.hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ if (IMContext imc{ MainHWND() }) {
+ imc.Notify(true);
}
break;
}
@@ -3306,34 +3339,16 @@ void ScintillaWin::ImeStartComposition() {
if (caret.active) {
// Move IME Window to current caret position
IMContext imc(MainHWND());
- const Point pos = PointMainCaret();
- COMPOSITIONFORM CompForm {};
- CompForm.dwStyle = CFS_POINT;
- CompForm.ptCurrentPos = POINTFromPoint(pos);
+ if (!imc)
+ return;
- ::ImmSetCompositionWindow(imc.hIMC, &CompForm);
+ imc.SetCompositionWindow(PointMainCaret());
// Set font of IME window to same as surrounded text.
if (stylesValid) {
// Since the style creation code has been made platform independent,
// The logfont for the IME is recreated here.
- const int styleHere = pdoc->StyleIndexAt(sel.MainCaret());
- LOGFONTW lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L""};
- int sizeZoomed = vs.styles[styleHere].size + (vs.zoomLevel * FontSizeMultiplier);
- if (sizeZoomed <= 2 * FontSizeMultiplier) // Hangs if sizeZoomed <= 1
- sizeZoomed = 2 * FontSizeMultiplier;
- // The negative is to allow for leading
- lf.lfHeight = -::MulDiv(sizeZoomed, dpi, pointsPerInch*FontSizeMultiplier);
- lf.lfWeight = static_cast<LONG>(vs.styles[styleHere].weight);
- lf.lfItalic = vs.styles[styleHere].italic ? 1 : 0;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfFaceName[0] = L'\0';
- if (vs.styles[styleHere].fontName) {
- const char* fontName = vs.styles[styleHere].fontName;
- UTF16FromUTF8(std::string_view(fontName), lf.lfFaceName, LF_FACESIZE);
- }
-
- ::ImmSetCompositionFontW(imc.hIMC, &lf);
+ imc.SetCompositionFont(vs, pdoc->StyleIndexAt(sel.MainCaret()), dpi);
}
// Caret is displayed in IME window. So, caret in Scintilla is useless.
DropCaret();
@@ -3389,10 +3404,10 @@ LRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {
rc->dwTargetStrOffset =rc->dwCompStrOffset;
IMContext imc(MainHWND());
- if (!imc.hIMC)
+ if (!imc)
return 0;
- if (!::ImmSetCompositionStringW(imc.hIMC, SCS_QUERYRECONVERTSTRING, rc, rcSize, nullptr, 0))
+ if (!imc.SetCompositionString(SCS_QUERYRECONVERTSTRING, rc, rcSize))
return 0;
// No selection asks IME to fill target fields with its own value.
@@ -3452,7 +3467,7 @@ LRESULT ScintillaWin::ImeOnDocumentFeed(LPARAM lParam) const {
memcpy(rcFeedStart, rcFeed.data(), rcFeedLen);
IMContext imc(MainHWND());
- if (!imc.hIMC)
+ if (!imc)
return 0;
DWORD compStrLen = 0;