質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

2099閲覧

自作Edit Controlを作成する上での問題点

Weapon

総合スコア106

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/03/27 06:58

前提・実現したいこと

Edit Controlで垂直中央寄せをするためteratail:WinAPI エディットコントロールのプロシージャにおいてサブクラス化やReact/Wineなど聞きましたがReactのWM_PAINTを参考にサブクラス化する方法が思いつかなかったので一からつくることにしました.
(以下のコードはサブクラス化をいじってみたのですが元のウィンドウがでてきてしまいます.)

自作する上での疑問点なのですが

  • アクティブ時の入力カーソル
  • 文字列の変換
  • 文字列の選択

です.

一つ目のアクティブ時の入力カーソルですが
文字入力時に横に出るカーソルはどのような技術・関数で作られているのでしょうか?GDIで自分で描画する必要があるのでしょうか?

二つ目の文字列の変換は
Reactのコードで見たようにIMMのAPIを用いればいいのでしょうか?

三つ目ですが
DrawTextやTextOutされた文字は選択することができません.
とはいえブラウザなど様々なUIにおいて選択できるものが(選択できないほうがいいものも含めて)たくさんあります.これはほかの関数やAPIの形で提供されているものなのでしょうか?それとも標準のEdit Controlでない限りマウスキャプチャーと背景色変更で自己実装する必要があるのでしょうか?

サブクラス化したコード

C

1#define UNICODE 2 3#include <Windows.h> 4#include <Windowsx.h> 5 6LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); 7LRESULT CALLBACK EditProc(HWND, UINT, WPARAM, LPARAM); 8 9#define ID_BUTTON 101 10 11const wchar_t CLASS_NAME[] = L"CLASS NAME"; 12 13HINSTANCE hGlobalInstance; 14WNDPROC getEditControl; 15 16int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR nCmdLine, int nCmdShow) { 17 UNREFERENCED_PARAMETER(hPrevInstance); 18 UNREFERENCED_PARAMETER(nCmdLine); 19 20 hGlobalInstance = hInstance; 21 22 WNDCLASSEX wc = { 23 sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW, WindowProc, 24 0, 0, hInstance, 25 NULL, (HCURSOR)LoadCursor(NULL,IDC_ARROW), (HBRUSH)GetStockObject(WHITE_BRUSH), 26 NULL, CLASS_NAME, NULL 27 }; 28 RegisterClassEx(&wc); 29 30 HWND hwnd = CreateWindowEx( 31 0, CLASS_NAME, L"Title", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 32 CW_USEDEFAULT, CW_USEDEFAULT, 960, 540, 33 NULL, NULL, hInstance, NULL 34 ); 35 36 ShowWindow(hwnd, nCmdShow); 37 UpdateWindow(hwnd); 38 39 MSG msg = {}; 40 while (GetMessage(&msg, NULL, 0, 0) > 0) { 41 TranslateMessage(&msg); 42 DispatchMessage(&msg); 43 } 44 45 return (int)msg.wParam; 46} 47 48LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 49 switch (uMsg) { 50 case WM_CREATE: 51 { 52 HWND hedit = CreateWindowEx( 53 0, L"Edit", L"Hello World", ES_CENTER | WS_CHILD | WS_VISIBLE | WS_BORDER, 54 160, 160, 190, 60, 55 hwnd, (HMENU)ID_BUTTON, hGlobalInstance, 0 56 ); 57 58 /* 59 TEXTMETRIC tm = {}; 60 ZeroMemory(&tm, sizeof(TEXTMETRIC)); 61 GetTextMetrics(GetDC(hedit), &tm); 62 63 wchar_t mes[1024] = {}; 64 wsprintf(mes, L"tmHeight : %d\ntmAscent : %d\ntmDescent : %d\n", tm.tmHeight, tm.tmAscent, tm.tmDescent); 65 MessageBox(hwnd, mes, L"RESULT", MB_OK); 66 */ 67 68 getEditControl = (WNDPROC)GetWindowLong(hedit, GWL_WNDPROC); 69 70 SetWindowLong(hedit, GWLP_WNDPROC, (LONG)EditProc); 71 } 72 break; 73 74 case WM_DESTROY: 75 PostQuitMessage(0); 76 break; 77 } 78 79 return DefWindowProc(hwnd, uMsg, wParam, lParam); 80} 81 82LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 83 switch (uMsg) { 84 case WM_KEYDOWN: 85 SendMessage(hwnd, WM_PAINT, 0, 0); 86 break; 87 88 case WM_PAINT: 89 90 wchar_t inputstring[1024]; 91 ZeroMemory(inputstring, sizeof(wchar_t) * 1024); 92 GetWindowText(hwnd, inputstring, 1024); 93 94 PAINTSTRUCT eps = {}; 95 HDC ehdc = BeginPaint(hwnd, &eps); 96 TextOut(ehdc, 30, 30, inputstring, lstrlen(inputstring)); 97 EndPaint(hwnd, &eps); 98 99 break; 100 } 101 102 return CallWindowProc(getEditControl, hwnd, uMsg, wParam, lParam); 103}

補足情報

Windows10 Pro
VisualStudio2017 Community

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Edit Control の自作は 10 年以上前にゲームを作成していた時は頻繁にやってましたね。
ゲーム画面に表示されるチャットの入力欄なんてのは完全にレンダリングで作成した Edit Control になり、ゲームの操作と両立させるために標準のコトンロールは使いづらかったです。と言うか標準コントロールのグラフィックスがゲーム画面とマッチしないため、そのまま使用するという選択の余地はありませんでしたが。
当時は DirectX のサンプルにそのまんま Edit Control があった気がします。IME 制御のサンプルとしても優秀でした。

一つ目のアクティブ時の入力カーソルですが

通常のウィンドウの場合、Caret 操作系の API を使用します。
CreateCaret でキャレットを作成し、DestroyCaret で作成したキャレットを破棄します。

WM_SETFOCUS 時に ShowCaret でキャレットを表示し、WM_KILLFOCUS 時に HideCaret でキャレットを非表示にします。

文字の入力や削除時に適宜 SetCaretPos でキャレット位置を更新します。

なお、DirectX や OpenGL で描画しているサーフィスで入力欄を実現しようとすると上記の API では実現できず、その場合はキャレット相当の画像を自身で描画することになります。OpenGL はわかりませんが、DirectX の方はサンプルと言うか UI 部品としてソース提供されていたと思います。

二つ目の文字列の変換は

必要に応じて IMM のメソッドを呼び出すことになりますが、通例 IME の API を直接呼び出す必要はありません。UNICODE でウィンドウを作成している場合、WM_CHAR で IME 確定後の文字(列)が通知されるので、WM_CHAR を処理していれば問題ありません。WM_SETFOCUS でフォーカスを受け取るように実装していれば、自動的に IME が有効になるかと思います。 ReactOS のソースは通常のテキストボックスと同様に変換中の文字列を入力位置に表示するためのものです。IME 自身が持つ変換ウィンドウを使用する場合、特にこれらの処理を実装する必要はありません。

三つ目ですが

これは API 等で提供されるものではありません。ReactOS がそうなっているように選択中の文字列を選択中であることを示すために描画する必要があります。ゲームの場合、手抜きしてマウス選択は受け付けないとかはありかと思いますが、通常のコントロールではマウスでの選択とキーボード操作の両方を処理するため、けっこうめんどくさい処理になります。なお、標準の Edit Control ですらダブルクリックで単語選択等を実装しているので、動作を近づけようとすると ReactOS の内容のままになると思います。


(2019/03/30 追記)

軽く書いてみたのですがWM_SETFOCUSはウィンドウをクリックしたら飛ぶものではないのですか?WM_LBUTTONDOWNでSetFocusしたら上手くいきましたが

アクティブとフォーカスは別ということですか?

Windows においてはアクティブ(ウィンドウ)とフォーカス(が設定されたウィンドウ)は意味が異なります。アクティブウィンドウはトップレベルウィンドウのうち選択されているものを指します。フォーカスはすべてのウィンドウのうちキーボードメッセージを受け取る対象のウィンドウのことを指します。

結論から言うと ReactOS のソースがそうなっているように子ウィンドウでは WM_LBUTTONDOWN 等のマウスメッセージで SetFocus を呼び出す必要があります。WM_LBUTTONDOWN 以外では WM_MOUSEACTIVATE で SetFocus することも可能ですが、これはあまりお勧めしません。

以下余談ですが、トップレベルウィンドウでは WM_MOUSEACTIVATE を DefWindowProc に渡すと自動的にフォーカスが設定されるため、子ウィンドウの処理とは異なるアプローチが可能です。逆に子ウィンドウ側で WM_MOUSEACTIVATE を処理するようにするとトップレベルウィンドウがアクティブ化する時の挙動がおかしくなります。ReactOS のソースでは Edit Control をトップレベルウィンドウにした場合、WM_MOUSEACTIVATE でフォーカスが設定されるため、フォーカスが当たっていない場合のみ SetFocus を呼び出すようになっているものと推測されます。

なお、以下のマイクロソフトのページの下の方に Edit Control で処理するメッセージについて記載がありますが、どのタイミングで SetFocus を呼び出しているかについては言及されていません。が、WM_MOUSEACTIVATE を処理していない以上、WM_LBUTTONDOWN で処理していると推測するしかない感じですね。
https://docs.microsoft.com/en-us/windows/desktop/controls/about-edit-controls

投稿2019/03/27 16:57

編集2019/03/30 07:24
atata0319

総合スコア881

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Weapon

2019/03/29 03:46

軽く書いてみたのですがWM_SETFOCUSはウィンドウをクリックしたら飛ぶものではないのですか?WM_LBUTTONDOWNでSetFocusしたら上手くいきましたが アクティブとフォーカスは別ということですか?
atata0319

2019/03/30 07:24

追記しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問