タイトルバーにタブを置く方法を試してみる。
Chromeのようにタブをタイトルバーに置きたいので、「Custom Window Frame Using DWM」ここに書いてある方法を試してみた。
手順としては下記の通りだ。
WM_CREATEメッセージをハンドルし、SWP_FRAMECHANGEDフラグをセットしSetWindowPos()でWM_NCCALCSIZEメッセージを発生させ、クライアント領域をウィンドウ領域いっぱいに拡張する。(MSの解説)
実施結果は下の画面のとおり。
クライアント領域を黒く塗りつぶすようにしているので、ウィンドウ全体が黒く塗りつぶされる。
DwmExtendFrameIntoClientArea()でフレーム領域をクライアント領域に拡張する。 (MSの解説)
実行するとクライアント領域内にウィンドウフレームが描画されるようになる。
クライアント領域をいっぱいに拡張してもフレーム操作ができるようにWM_NCHITTESTメッセージをカスタマイズする。(MSの解説)
見た目は普通のウィンドウだが、クライアント領域がウィンドウ全体を覆っているため、クライアント領域外イベントが発生しなくなる。つまりウィンドウサイズ変更やキャプションボタン操作、タイトルバーをドラッグしての移動などができなくなる。なのでクライアント領域内のフレームでクライアント領域外イベントを発生させるエミュレーションコードをWM_NCHITTESTメッセージをハンドルして実装する。キャプションボタンイベントはDwmDefWindowProc()を呼び出すことでエミュレートする。 (MSの実装例)
これでウィンドウの見た目や機能を維持しつつも、クライアント領域をウィンドウいっぱいに拡張することができた。ウィンドウ内の任意の位置にコントロールを配置できるようになるので、タブコントロールのタブ部分をタイトルバーにうまく重なるようにすればChrome風のUIが実現できる。
タブコントロールの自前描画
そのままタブコントロールを配置すると下のようになる。
確かにタイトルバーの上にタブが配置されるが、タブ描画のフレーム塗りつぶしによりタイトルバーが隠れてしまう。背景色のみを透明にしたいが、いろいろ調べたがわからず、どうも自前描画するしかないようである。
自前描画の方法について考える
さて自前描画の方法であるが、以前試そうと思っていたウィンドウ・フックを使ってウィンドウ内に表示されるコントロールの描画メッセージを横取りする方法で行おうかなと思っている。実際の描画は親ウィンドウのサーフェースにDirect2D/Direct3Dで描画する。GDIで描画するのはWindows Vista以降ではソフトウェアエミュレーションとなっているしパフォーマンスダウンが気になるしね。
試しにウィンドウのhwndを引数にスワップチェインを作成し、表示してみたのが下の画面である。
クライアント領域がウィンドウいっぱいに広がっているのでフレーム部分も全部書き換わってしまうのである。もしこのままやるとすると、フレームもすべて再描画しなくてはいけなくなる。さらに問題なのはコントロール1つ描くごとにPrensent()すると都度画面が書き換わりおかしくなってしまうのである。
それを解決するための手段としては
DXGIのDXGI_PRESENT_PARAMETERSのDiretyRectsを使う。
これを使用すると今回更新した部分のみをPresent()することが可能となる。ただし使用するにはドライバサポートおよび使用できるスワップチェインに制限がある。
オフスクリーンサーフェースを作ってそこにバッファリングし、VSyncかタイマーで定期的にバッファを描画する。
自前で実装しても良いがこれを行う仕組みとしてDirectCompositionがあるのでそれを利用して実装する。
上記2種類の方法をこれから試してみようかなと思っている。二番目の方法だとおそらく既存フレーム描画とオフスクリーンサーフェースとのα合成ができそうなのでこちらの方が有望だが、一番目の方法も技術的な興味があるので両方試してみることにする。