ab.com コミュニティ https://www.activebasic.com/forum/ |
|
EditboxにおけるIME変換中(漢字変換中)の背景色変更 https://www.activebasic.com/forum/viewtopic.php?t=4579 |
ページ 1 / 1 |
作成者: | 淡幻星 [ 2015年9月29日(火) 12:54 ] |
記事の件名: | EditboxにおけるIME変換中(漢字変換中)の背景色変更 |
EditboxにおけるIME変換中(漢字変換中)の背景色を変更する方法。 合わせて、通常時のEditBoxの背景色変換にも対応(こっちはオマケ)。 使い方は、下記のコード冒頭のコメントを参照ください。 なお、処理の詳細はこちら↓に記載。興味あればどうぞ~。 WM_IME_COMPOSITIONを掴まえた後の具体的な処理について コード: /* [MngBackgoundColorText.sbp] 背景色を管理するクラスの定義。 Fontは窓に紐づくが、背景色は描画のタイミングで明示的に設定するものなので、 必ずしも紐づける必要はない。--> このクラスで別管理する。 ※IME変換中の文節などは、下線ではなく背景色で対応。変換中の項目を青味かける。 【使い方】 1.以下のメソッドを用いて、背景色とフォントを設定する。 SetColorForTextOnExitBox( dwTextColorNew As DWord, dwTextLigthColorNew As DWord ) SetBackgoundColorForTextOnExitBox( dwColorTextBackgroundNew As DWord ) SetLogiaclFontForIME( ByRef stLogFont As LOGFONT ) 2.WM_CTLCOLOREDITメッセージを捉まえたら、以下のメソッドを呼び出して、 その戻り値をreturnする。 Function WndProcFor_WM_CTLCOLOREDIT( hDC As HDC, hEdit As HWND ) As Long 3.下記のIME関連のメッセージを捉まえたら、 → WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_NOTIFY 以下のメソッドを呼び出す。 Function ImmProc( hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM ) As Char このメソッドが「FALSE」を返した時はデフォルト動作なので、スルーすること(デフォルト窓プロシージャへ流す)。 '// 一方で「TRUE」を返した時は、独自処理済みなので窓プロシージャを戻り値0で抜けること。 ※IME変換中の背景色は、固定値で itsRgbColorImmBackground で持っているので、 必要であれば変更のこと。 以上ー。 */ '// 各種、必要なWinAPI32の宣言と関数定義。 '// #include にも一部あるが、不足なので、ここで明示的に宣言する。 TypeDef HIMC = DWord Declare Function ImmSetCompositionFont Lib "imm32" Alias "ImmSetCompositionFontA" (hIMC As HIMC, ByRef lplf As LOGFONT) As Long Const CFS_DEFAULT = &H0000 Const CFS_RECT = &H0001 Const CFS_POINT = &H0002 Const CFS_FORCE_POSITION = &H0020 Const CFS_CANDIDATEPOS = &H0040 Const CFS_EXCLUDE = &H0080 Type COMPOSITIONFORM dwStyle As Long ptCurrentPos As POINTAPI rcArea As RECT End Type Declare Function ImmSetCompositionWindow Lib "imm32" (hIMC As HIMC, ByRef lpCompForm As COMPOSITIONFORM) As Long Declare Function ImmGetContext Lib "imm32" (hWnd As HWND) As Long Declare Function ImmReleaseContext Lib "imm32" (hWnd As HWND, hIMC As HIMC) As Long '// +++ ここから、api_imm.sbp に無い宣言を自前で追加 +++ Const GCS_RESULTSTR = &H0800 Const GCS_COMPSTR = &H0008 Const GCS_COMPATTR = &H0010 Const GCS_DELTASTART = &H0100 '// attribute for COMPOSITIONSTRING Structure Const ATTR_INPUT = &H00 Const ATTR_TARGET_CONVERTED = &H01 Const ATTR_CONVERTED = &H02 Const ATTR_TARGET_NOTCONVERTED = &H03 Const ATTR_INPUT_ERROR = &H04 Const ATTR_FIXEDCONVERTED = &H05 Declare Function ImmGetCompositionString Lib "imm32" Alias "ImmGetCompositionStringA" (hIMC As HIMC, dwIndex As DWord, lpBuf As BytePtr, dwBufferSize As DWord ) As Long Const IMM_ERROR_NODATA = -1 Const IMN_OPENCANDIDATE = &H0005 Type CANDIDATEFORM dwIndex As DWord dwStyle As DWord ptCurrentPos As POINTAPI rcArea As RECT End Type Declare Function ImmSetCandidateWindow Lib "imm32" (hIMC As HIMC, ByRef lpCandidateFoem As CANDIDATEFORM) As Char '// --- ここまで(imm.hを参照して定義 [C:\borland\bcc55\Include\imm.h] ) --- '// ※Web上で参照するなら http://katahiromz.web.fc2.com/mathai/immdev.h 辺り。 '// +++ 以下も追加 +++ Declare Function ExtTextOut Lib "Gdi32" Alias "ExtTextOutA" ( hDC As HDC, nXStart As Long, nYStart As Long, dwOptions As DWord, ByRef lpRect As RECT, lpString As BytePtr, cbString As Long, lpDx As *Long) Const ETO_CLIPPED = &H0004 '// --- ここまで --- #include '// WsColorVector クラスを利用のため。→外部に公開時はソコを削除しようか。 Class ManagementAndDrowTextBackGroundColor itsBrushTextBackground As HBRUSH itsRgbColorTextBackground As DWord itsRgbColorText As Dword itsRgbColorTextAtNotFocused As Dword itsImmLastLength As DWord itsImmLastLengthMax As DWord itsLogFontIME AS LOGFONT itsLogFontIME_IsEnable As Char itsRgbColorImmBackground As DWord '暫定処置(変換時の属性処理が不完全) '// ※将来に、calloc() を Heep~に置き換える考慮でここにまとめておく。 Function _HeapWorikngArea( dwSize As DWord ) As VoidPtr _HeapWorikngArea = calloc( dwSize ) End Function Sub _FreeWorkingArea( pMark As VoidPtr ) free( pMark ) End Sub Public Sub ManagementAndDrowTextBackGroundColor() 'テキストの色と背景色(任意に変えることを考慮して追加) itsBrushTextBackground = NULL '処理の共通化のために、NULLで初期化 This.SetColorForTextOnExitBox( RGB( &h00,&h00,&h00 ), DISABLED_RGB ) This.SetBackgoundColorForTextOnExitBox( RGB( &hFF,&hFF,&hFF ) ) 'IMMの変換領域の初期値 itsImmLastLength = 0 itsRgbColorImmBackground = RGB( &H44, &H44, &H44 ) '// 変換中の背景色【暫定固定値】 itsLogFontIME_IsEnable = FALSE End Sub Sub ~ManagementAndDrowTextBackGroundColor() If itsBrushTextBackground <> NULL Then DeleteObject( itsBrushTextBackground ) End If End Sub '文字色を設定(エディット窓の文字色+非フォーカス時の淡色)。 Sub SetColorForTextOnExitBox( dwTextColorNew As DWord, dwTextLigthColorNew As DWord ) itsRgbColorText = dwTextColorNew itsRgbColorTextAtNotFocused = dwTextLigthColorNew End Sub '文字の背景色を設定(エディット窓の背景色) Sub SetBackgoundColorForTextOnExitBox( dwColorTextBackgroundNew As DWord ) itsRgbColorTextBackground = dwColorTextBackgroundNew If itsBrushTextBackground <> NULL Then DeleteObject( itsBrushTextBackground ) End If itsBrushTextBackground = CreateSolidBrush( itsRgbColorTextBackground ) End Sub '文字色の取得 Function GetColorForTextOnExitBox() As DWord GetColorForTextOnExitBox = itsRgbColorText End Function '文字の背景色の取得 Function GetBackgoundColorForTextOnExitBox() As DWord GetBackgoundColorForTextOnExitBox = itsRgbColorTextBackground End Function '非フォーカス時の淡色化の有無を取得 Function IsTextColorUseLight() IsTextColorUseLight = (DISABLED_RGB <> itsRgbColorTextAtNotFocused) End Function 'IME変換中に利用するフォントの「論理フォント構造体」を設定する。→内部でコピーして保持する。 Sub SetLogiaclFontForIME( ByRef stLogFont As LOGFONT ) memcpy( VarPtr(itsLogFontIME), VarPtr(stLogFont), sizeOf(LOGFONT) ) itsLogFontIME_IsEnable = TRUE End Sub '// 親窓に WM_CTLCOLOREDIT が通知された時の動作。 '// ※lParam As HWND, wParam As HDC で対象が渡されてくるので、 '// 適切に文字色と背景色を設定して返すと、その色でテキストを描画してくれる。 '// ref.[コントロールの背景色/フォント変更] - http://www50.tok2.com/home2/StillGreen/ ... kcolor.htm Function WndProcFor_WM_CTLCOLOREDIT( hDC As HDC, hEdit As HWND ) As Long 'テキストの文字色 SetBkMode( hDC, OPAQUE ) '背景を塗りつぶしモードに設定 If itsRgbColorTextAtNotFocused = DISABLED_RGB Then '非フォーカス時の扱いで分岐 SetTextColor( hDC, itsRgbColorText ) Else If GetFocus() = hEdit Then SetTextColor( hDC, itsRgbColorText ) Else SetTextColor( hDC, itsRgbColorTextAtNotFocused ) End If End If 'テキストの背景色 SetBkColor( hDC, itsRgbColorTextBackground ) 'for テキストが書かれている部分 WndProcFor_WM_CTLCOLOREDIT = itsBrushTextBackground As Long 'for テキストが書かれていない部分 End Function Private Function IsDarkColorBackgroundForIME() Dim colorCls As WsColorVector( itsRgbColorTextBackground ) Dim xBrightness As Single xBrightness = colorCls.GetBrightness() '0.0~1.0で規格化された〝輝度" If xBrightness < 0.4 Then '充分にブラックに近い場合は(輝度が低い)Yesとする。 IsDarkColorBackgroundForIME = TRUE '変換中背景色は固定的に下記を用いる。 itsRgbColorImmBackground = RGB( &H44, &H44, &H44 ) Else IsDarkColorBackgroundForIME = FALSE End If End Function Public '// エディットボックスをサブクラス化して、IME関連の下記のメッセージを捉まえたときの動作。 '// --> WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_NOTIFY '// (※通知先は、親窓かもしれない?その場合はサブクラス化不要) '// '// この関数が「FALSE」を返した時はデフォルト動作なのでスルーすること。 '// 一方で「TRUE」を返した時は、独自処理済みなので窓プロシージャを戻り値0で抜けること。 Function ImmProc( hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM ) As Char Dim hImc As HIMC ImmProc = FALSE 'Win Vistaより前では、IMM関連の挙動が異なるので弾く。 If OS_Vista > GetOsType() Then Exit Function End If '【TODO】変換中属性の処理が不完全なので、一定値以下の黒背景でのみ、本関数を有効にする。 '※ここを抜けた時点で、itsRgbColorImmBackground は設定済み。 If FALSE = IsDarkColorBackgroundForIME() Then Exit Function End If '// IMMコンテキストを取得 hImc = ImmGetContext( hWnd ) If hImc = NULL Then Exit Function End If Select Case dwMsg '// 文字変換開始 Case WM_IME_STARTCOMPOSITION ImmProc = ImeStartComposition( hWnd, hImc, wParam, lParam ) '// 文字変換処理中 Case WM_IME_COMPOSITION If lParam = 0 Then '// 変化はあったがlParam=0(変化の内容が未定義)は、 '// 「1byteのみ入力で、やっぱキャンセル」された状態の様子(仕様見つからず。実機確認)。 '// 一旦「WM_IME_ENDCOMPOSITION」扱いする。 '// …で、本来は「ImeEndComposition()」を呼びたいのだが、再描画範囲(itsImmLastLengthMax)が '// ただしく機能していないようなので、、、無理やりだが「全体再描画」で対応。 '// 【ToDo】もう少しスマートな再描画をしたい。at 2015.09.02 InvalidateRgn( hWnd, NULL, TRUE ) Else ImmProc = ImeComposition( hWnd, hImc, wParam, lParam ) End If '// 文字変換終了 Case WM_IME_ENDCOMPOSITION ImmProc = ImeEndComposition( hWnd, hImc, wParam, lParam ) '// その他のIME関連の通知 Case WM_IME_NOTIFY Select Case wParam '// 変換候補リストが表示されようとしている Case IMN_OPENCANDIDATE ImmProc = ImmSetCandidateWindowPos( hWnd, hImc ) Case Else ImmProc = FALSE End Select Case Else ImmProc = FALSE End Select '// IMMコンテキストを解放 ImmReleaseContext( hWnd, hImc ) End Function Private Function ImeStartComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM ) As Char itsImmLastLength = 0 '// 内部変数を初期化 itsImmLastLengthMax = 0 '// 内部変数を初期化 ImeStartComposition = FALSE '// IMEに対しては何もしない(デフォルト動作)。 End Function Function ImeEndComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM ) As Char Dim hDC As HDC Dim stPoint As POINTAPI Dim stRect As RECT Dim fontExtentPoint As SIZE Dim data As Word '// 変換窓で描画した範囲を「再描画」して後始末する。 hDC = GetDC( hWnd ) data = &HA4A4 '// 全角の「い」に相当。 GetTextExtentPoint32( hDC, VarPtr(data), sizeOf(Word), fontExtentPoint ) GetCaretPos( stPoint ) stRect.right = stPoint.x stRect.top = stPoint.y stRect.left = stPoint.x + itsImmLastLengthMax stRect.bottom = stPoint.y + fontExtentPoint.cy InvalidateRect( hWnd, stRect, TRUE ) ReleaseDC( hWnd, hDC ) ImeEndComposition = FALSE '// IMEに対しては何もしない(デフォルト動作)。 End Function Function ImeComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM ) Dim pszImmString As BytePtr Dim pAttrArray As *Char ImeComposition = FALSE pszImmString = NULL If (lParam and GCS_RESULTSTR) <> 0 Then '// 文字列の変換状態が全て確定した場合に GCS_RESULTSTRフラグがONになる '// →確定した文字列を取得する。 pszImmString = GetCompositionString( hImc, GCS_RESULTSTR ) If pszImmString <> NULL Then '// 呼び出し元のエディット窓に対して、確定文字列を挿入する。 SendMessage( hWnd, EM_REPLACESEL, TRUE, pszImmString ) '// 利用した領域を解放 _FreeWorkingArea( pszImmString ) '// IMEに対して独自処理をしたとして、デフォルト動作をSkipするよう設定。 ImeComposition = TRUE End If End If '// ※「確定」状態で且つ、次の「未確定(=変換状態に変化)がある」状態もあるので留意。 '// ↑↓なので★続けて★処理する。 If (lParam and GCS_COMPSTR) <> 0 Then '// 変換状態に何か変化があった場合に GCS_COMPSTRフラグがONになる '// →変換中(IME途中)の文字列を取得する。 pszImmString = GetCompositionString( hImc, GCS_COMPSTR ) If pszImmString <> NULL Then '// 文字列が取れたなら、その属性(下記の5種類)を取得する(1文字当たり8bitの定数)。 '// ・選択されていなく、変換されていない文字。:ATTR_INPUT '// ・選択されていて、変換されている文字。 '// ・選択されていなく、変換されている文字。 '// ・選択されていて、変換されていない文字。 '// ・無効な文字 pAttrArray = GetCompositionString( hImc, GCS_COMPATTR ) As *Char '// 変換中の文字列を、IME窓へ明示的に表示(IME窓=Composition窓) '// →背景色などを任意にできる♪ DisplayCompositionString( hWnd, pszImmString, pAttrArray ) '// 利用した領域を解放 _FreeWorkingArea( pszImmString ) If pAttrArray <> NULL Then _FreeWorkingArea( pAttrArray ) End If '// IMEに対して独自処理をしたとして、デフォルト動作をSkipするよう設定。 ImeComposition = TRUE End If End If End Function '// ImmGetCompositionStringA()に対するラッパー関数。【Unicodeは未考慮】 Function GetCompositionString( hImc As HIMC, dwType As DWord ) As BytePtr Dim nSize As Long Dim pszImmString As BytePtr nSize = ImmGetCompositionString( hImc, dwType, NULL, 0 ) if nSize > 0 Then pszImmString = _HeapWorikngArea( nSize +1 ) ImmGetCompositionString( hImc, dwType, pszImmString, nSize ) pszImmString[ nSize ] = NULL '// この文字列( or 属性配列)がNULL文字で終わるとは限らない。 '// 従って、bufに返された文字列の長さを戻り値によって判定しなければならない。 '// http://nienie.com/~masapico/api_ImmGetC ... tring.html '// →文字列と仮定して、NULLを終端に追加することで、返却先に終端を伝えるようにしようか。 '// ただし、属性配列はATTR_INPUT=0x00なので、終端の区別が出来ない。利用時は注意。 End If GetCompositionString = pszImmString End Function '// IME変換中文字列を自前で描画する。 '// ⇒これにより、背景色の設定などが可能になる。 Sub DisplayCompositionString( hWnd As HWND, pszImmString As BytePtr, pAttrArray As *Char ) Dim hDC As HDC Dim stPoint As POINTAPI Dim stClientRect As RECT Dim stImeRect As RECT Dim hBrush As HBRUSH Dim hPen As HPEN Dim hFont As HFONT Dim fontExtentPoint As SIZE Dim data As Word hDC = GetDC( hWnd ) '【TODO】変換中の文字列である旨をユーザーへ伝える方法として、背景色を変更。 ' 通常のIMEだと下線とかフォント側の変更でやってるね。 ' 背景色の方が楽なので、暫定でこの方法。 hBrush = SelectObject( hDC, CreateSolidBrush( itsRgbColorImmBackground ) ) hPen = SelectObject( hDC, CreatePen( PS_SOLID, 1, itsRgbColorImmBackground ) ) If TRUE = itsLogFontIME_IsEnable Then '【TODO】本来は、属性に応じて一文字ごとに下線の有り無し等設定すべきだが、一括設定で暫定。 hFont = SelectObject( hDC, CreateFontIndirect( itsLogFontIME ) ) End If data = &HA4A4 '// 全角の「い」に相当。 GetTextExtentPoint32( hDC, VarPtr(data), sizeOf(Word), fontExtentPoint ) GetCaretPos( stPoint ) GetClientRect( hWnd, stClientRect ) If itsImmLastLength > 0 Then '// 背景を塗りつぶす(だいぶ無理やり…) stImeRect.left = stPoint.x stImeRect.top = stPoint.y stImeRect.bottom = stPoint.y + fontExtentPoint.cy stImeRect.right = stPoint.x + itsImmLastLength If stImeRect.right > stClientRect.right Then stImeRect.right = stClientRect.right -1 End If Rectangle( hDC, stImeRect.left, stImeRect.top, stImeRect.right, stImeRect.bottom ) If itsImmLastLength > itsImmLastLengthMax Then itsImmLastLengthMax = itsImmLastLength End If End If itsImmLastLength = ( lstrlen( pszImmString ) +1 ) * (fontExtentPoint.cx / 2) '// 横幅を更新 _TextOutWithAttr( hDC, VarPtr(stPoint), VarPtr(fontExtentPoint), stClientRect.right, pszImmString, pAttrArray ) DeleteObject( SelectObject( hDC, hBrush ) ) '// HDCから解放したBRUSHは、本関数内で作成したものなので破棄する。 DeleteObject( SelectObject( hDC, hPen ) ) '// HDCから解放したPENは、本関数内で作成したものなので破棄する。 If TRUE = itsLogFontIME_IsEnable Then DeleteObject( SelectObject( hDC, hFont ) ) '// HDCから解放したFONTは、本関数内で作成したものなので破棄する。 End If ReleaseDC( hWnd, hDC ) End Sub '// IME属性(選択中、変換中etc)に応じて異なる背景色で表示 Sub _TextOutWithAttr( hDC As HDC, pstPoint As *POINTAPI, pFontExtentPoint As *SIZE, nMaxClientRightPos As Long, pszImmString As BytePtr, pAttrArray As *Char ) Dim textLength As DWord Dim stImeRectParts As RECT Dim nX As Long Dim nY As Long Dim rgbList[ 6 ] As DWord Dim sameAttrLength As DWord Dim pMarkStart As BytePtr Dim nDX As DWord Dim i As DWord '// 【暫定】IME変換中の背景色を元に、変換対象や選択状態の背景色を生成。 rgbList[ ATTR_INPUT ] = itsRgbColorImmBackground '// RGB( &H44, &H44, &H44 ) '// 入力直後 rgbList[ ATTR_TARGET_CONVERTED ] = itsRgbColorImmBackground + RGB( &H33, &H33, &H77 ) '// 変換中!→青味かける。 rgbList[ ATTR_CONVERTED ] = itsRgbColorImmBackground '// 変換中だけど未選択。→「入力直後」と同じ色にする。 rgbList[ ATTR_TARGET_NOTCONVERTED ] = itsRgbColorImmBackground '// 無い?→「入力直後」と同じ色にする。 rgbList[ ATTR_INPUT_ERROR ] = itsRgbColorImmBackground '// 無い?→「入力直後」と同じ色にする。 '// 参考サイトhttp://nienie.com/~masapico/api_ImmGetCompositionString.html SetTextColor( hDC, itsRgbColorText ) textLength = lstrlen( pszImmString ) '// pAttrArrayにしたがって、『属性の切り替わる単位で』背景書を変換して書こうか。 '// http://nienie.com/~masapico/api_ImmGetC ... tring.html '// nDX = pFontExtentPoint->cx / 2 nX = pstPoint->x nY = pstPoint->y i = 0 pMarkStart = pszImmString While( i < textLength ) '// 属性の変わる直前までをピックアップ sameAttrLength = 1 While( (pAttrArray[ i ] = pAttrArray[ i+1 ]) and (i < textLength) ) '// 終端にNULLが入る仕様を利用(オーバーフローしない) sameAttrLength++ i++ Wend '// 属性単位で文字列を描画 SetBkColor( hDC, rgbList[ pAttrArray[ i ] ] ) stImeRectParts.left = nX stImeRectParts.top = nY stImeRectParts.right = nX + sameAttrLength * nDX stImeRectParts.bottom = nY + pFontExtentPoint->cy If stImeRectParts.right > nMaxClientRightPos Then stImeRectParts.right = nMaxClientRightPos -1 i = textLength '// ループを抜けるフラグとして設定。 End If ExtTextOut( hDC, nX, nY, ETO_CLIPPED, stImeRectParts, pMarkStart, sameAttrLength, NULL ) nX = stImeRectParts.right pMarkStart += sameAttrLength i++ Wend End Sub '// IME変換リストの表示位置を設定する。 Function ImmSetCandidateWindowPos( hWnd As HWND, hImc As HIMC ) As Char Dim stPoint As POINTAPI Dim stImmCandidateForm As CANDIDATEFORM GetCaretPos( stPoint ) ZeroMemory( VarPtr(stImmCandidateForm), Len(stImmCandidateForm) ) stImmCandidateForm.ptCurrentPos.x = stPoint.x stImmCandidateForm.ptCurrentPos.y = stPoint.y stImmCandidateForm.dwStyle = CFS_CANDIDATEPOS ImmSetCandidateWindow( hImc, stImmCandidateForm ) '// その後のIME動作はデフォルトの流れに任せる。 ImmSetCandidateWindowPos = FALSE End Function End Class |
ページ 1 / 1 | 全ての表示時間は UTC+09:00 です |
Powered by phpBB® Forum Software © phpBB Limited https://www.phpbb.com/ |