FC2ブログ

VS2017-はじめの1/10歩(12):タブレット画面の回転への対応

   プログラミング [2019/09/05]
だいぶ間が空いてしまいましたが、ポツポツながら再開します。

VS2017を使うんだけど慣れてないからVS6でまずは作ってました。
→ナレーション再生と音声認識を使う
  →そのため音声データの作り方(多言語対応)
  →ナレーション(.WAV)再生のサンプルコード
  →音声認識のサンプルコード
→VS6で作ったC/C++コードをVS2017に持って行って.cppレベルで共通にする方法
→ダイアログベースのプログラム
  →背景に画像(.BMP)を使う
  →ラベルを透かす
  →ボタンの色を変える
  →Windowsタブレット上でピンチイン/アウト:拡大縮小
  →タブレット画面の回転への対応←今回


前回、ピンチイン/アウトによるウィンドウ内容の拡大/縮小は、プログラム内に処理を入れるまでもなく、フリーのユティリティを追加インストールして間に合わすことにしました。

もう一つタブレットらしい機能として、タブレットの向きを重力方向に対して、縦横回転させると画面の内容もそれに合わせて回転する機能があります。

作成するアプリが、「縦横回転してもウィンドウを移動すりゃいい」くらいのサイズなら何も気にする必要はないい話。
でも、PCとは言ってもタブレットの画面サイズは小さい。老眼の出始めたさるくらいになると、タブレットの小さい文字は辛い。
なので、大きい文字にしたい。

そこで、考えたデザインの方針は、
・ダイアログは、普通のシステムデフォルトの文字サイズで作って、ダイアログ内のコントロール/文字の配置をデザインする。
 内容に可変部分がなければ、リソースエディタで作って済ますということ。
・表示内容の多い少ないはどうしても出てくるので、基本的にウィンドウ内の配置は内容に応じて縦に伸ばす。
・画面の方向が縦長/横長関わらず、横方向に合わせたフルサイズに拡大して、文字も拡大比率に合わせて大きくする。
・横フルサイズ表示、縦スクロール可能、ウィンドウの移動は不要とする。

結局、ピンチイン/アウト操作での拡縮は止めましたが、画面のサイズに応じての拡大縮小を実装することにしました。


まずは、基本的に画面配置の回転は誰がやってるのか?
→当然ですが、Windowsでした。
回転したときに、ウィンドウ(アプリ)側に何かイベントが来る?
→WM_DISPLAYCHANGEメッセージが来るらしい。

ということなので、ディスクトップサイズに合わせたアプリのウィンドウサイズ変更は、WM_INITDIALOGとWM_DISPLAYCHANGEのときにやればよい
ということになります。

※このあと、スクロール/スワイプで結構手間が掛かってますが、その詳細はまた別途説明するとして。

以下のようなコードでやりました。

◆ウィンドウプロシジャ内の処理
//----------------------------------------------
case WM_INITDIALOG:
{

//可変のコントロール作成、リスト/コンボの内容設定など

//スクリーンサイズに合わせて、各コントロールの位置/サイズ/フォントの変更
//※可変項目が全部整ってからやる。
RelocationControlles(hDlg, RELO_INIT,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
&g_hFntEmpEdit, &g_ScEdit);

//その他、ウィンドウレイアウトに関係ない初期化

return TRUE;
}
case WM_DESTROY:

//拡大時フォントオブジェクトの廃棄
if (g_hFntEmpEdit != NULL) {
DeleteObject(g_hFntEmpEdit);
g_hFntEmpEdit = NULL;
}

break;



//******* 解像度変更(回転)時処理
case WM_DISPLAYCHANGE:
{
INT scrn_w = LOWORD(lParam);
INT scrn_h = HIWORD(lParam);
RelocationControlles(hDlg, RELO_REIN, scrn_w, scrn_h, &g_hFntEmpEdit, &g_ScEdit);
InvalidateRect(hDlg, NULL, FALSE);
break;
}
//----------------------------------------------


実効の処理は・・・



◆ダイアログ内のコントロール類のサイズと配置/フォントサイズの変更
//----------------------------------------------
//name :RelocationControlles
//function :スクリーンサイズに合わせて、各コントロールの位置/サイズ
// /フォントを変更する
//parameter :hDlg -ダイアログハンドル
// nTyp -処理タイプ(全変更、再変更、フォントのみ)
// iScrW -スクリーン幅
// iScrH -スクリーン高さ
// hFntEmp -使用フォントハンドル
// pSc -スクロール情報
//global :なし
//return :なし

VOID RelocationControlles(
HWND hDlg,
INT nTyp,
INT iScrW,
INT iScrH,
HFONT *phFntEmp,
PMYSCROLLINF pSc)
{
RECT rect;
INT iscr_w = iScrW;
INT iscr_h = iScrH;
float flexp;
ENMCHLD_IO enmparam;
INT fullh;

//サイズ変更時は一旦スクロール位置を0に戻す
if (nTyp == RELO_REIN && pSc != NULL) {
//表示位置をy=0に戻す
//ScrollWindow(hDlg, 0, pSc->pos, NULL, NULL);
SendMessage(hDlg, WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 0), NULL);
}
//ダイアログサイズをフルスクリーンにした場合の倍率を求める。
GetWindowRect(hDlg, &rect);
flexp = (float) iscr_w / (rect.right - rect.left);
//標準フォントのサイズから拡大時フォントサイズを計算する(各ダイアログ共通)
if (nTyp != RELO_REIN) {
if (*phFntEmp == NULL) {
*phFntEmp = CreateMyFont(flexp, MY_FONT_FACE, NULL);
}
}
//再初期化時に拡張フォントを一旦解放する:CreateMyFont内
else {
*phFntEmp = CreateMyFont(flexp, MY_FONT_FACE, *phFntEmp);
}
//各コントロールのサイズ変更
enmparam.proc_type = nTyp;
enmparam.h_parent = hDlg;
enmparam.exp_rate = flexp;
enmparam.emp_font = *phFntEmp;
fullh = rect.bottom - rect.top;
enmparam.pbottom = &fullh;
EnumChildWindows(hDlg, EnumChildExpand, (LPARAM) &enmparam);

if (nTyp != RELO_FONT) {
if (pSc != NULL) {
//スクロール関連情報の初期化
ResetScrollInfo(pSc, iscr_h, fullh, flexp);
}
//ダイアログをフルスクリーンにする。WM_SIZEを発生させる
MoveWindow(hDlg, 0, 0, iscr_w, iscr_h, TRUE);
}

return;
}

//----------------------------------------------
//name :CreateMyFont
//function :現在のフォントを拡大したフォントオブジェクトの作成
// ※使い終わったときに、オブジェクトを廃棄する処理が必要なので要注意。
//parameter :iFontExp -現在のフォントに対する倍率
// pFontName -フォント名
// hNowFnt -現在のフォント
//global :なし
//return :作成したフォントハンドル

HFONT CreateMyFont(
float iFontExp,
PCHAR pFontName,
HFONT hNowFnt)
{
LONG fonth; //フォントの高さ
HFONT hsysfont; //システムフォントハンドル
LOGFONT fontform; //システムフォント情報

//システムフォント情報を取得
if (hNowFnt) {
GetObject((HGDIOBJ)hNowFnt, sizeof(LOGFONT), &fontform);
DeleteObject(hNowFnt);
}
else {
hsysfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
GetObject((HGDIOBJ)hsysfont, sizeof(LOGFONT), &fontform);
}
fonth = (LONG) roundf(iFontExp * fontform.lfHeight);

return CreateFont(
fonth, //フォント高さ
0, //文字幅
0, //テキストの角度
0, //ベースラインとx軸との角度
FW_BOLD, //フォントの重さ(太さ) //FW_REGULAR,
FALSE, //イタリック体
FALSE, //アンダーライン
FALSE, //打ち消し線
SHIFTJIS_CHARSET, //文字セット
OUT_DEFAULT_PRECIS, //出力精度
CLIP_DEFAULT_PRECIS, //クリッピング精度
PROOF_QUALITY, //出力品質
FIXED_PITCH | FF_MODERN, //文字の間隔:ピッチとファミリー
pFontName); //書体名
}

//----------------------------------------------
//name :EnumChildExpand
//function :子ウィンドウの位置/サイズ/フォントの変更
//parameter :hCtrl -コントロールのウィンドウハンドル
// lParam -パラメタ(上位:処理種別、下位:倍率x100)
//global :[r]g_szMsgBuf, [r]g_szMyTitle
//return :TRUE

BOOL CALLBACK EnumChildExpand(
HWND hCtrl,
LPARAM lParam)
{
RECT rect;
POINT pt;
INT x, y, w, h;
TCHAR classname[32];
PENMCHLD_IO penmparam = (PENMCHLD_IO) lParam;
float flexp = penmparam->exp_rate;

GetWindowRect(hCtrl, &rect); //スクリーン座標系
SendMessage(hCtrl, WM_SETFONT, (WPARAM)penmparam->emp_font, TRUE);

if (penmparam->proc_type != RELO_FONT) {
if (GetParent(hCtrl) == penmparam->h_parent) {
pt.x = rect.left;
pt.y = rect.top;
ScreenToClient(penmparam->h_parent, &pt); //親からの相対座標に変換
x = (INT)roundf(pt.x * flexp);
y = (INT)roundf(pt.y * flexp);
w = (INT)roundf((rect.right - rect.left) * flexp);
h = (INT)roundf((rect.bottom - rect.top) * flexp);
MoveWindow(hCtrl, x, y, w, h, TRUE); //親の相対座標系内で移動&リサイズ
//コンボボックスのサイズ設定
GetClassName(hCtrl, classname, sizeof(classname));
_strlwr_s(classname, sizeof(classname));
if (strcmp(classname, "combobox") == 0) {
SetComboSize(hCtrl, 5);
}
else if (strcmp(classname, "listbox") == 0) {
SetListboxHScrollSize(hCtrl, penmparam->emp_font);
}
else if (strcmp(classname, "syslistview32") == 0) {
SetListvwColumnSize(hCtrl, penmparam->emp_font);
}
//最大y座標の更新
if (*(penmparam->pbottom) < (y + h)) {
*(penmparam->pbottom) = y + h;
}
}
else {
//****debug->
//wsprintf(g_szMsgBuf, "子供じゃない? prm=%08x, parent=%08x", penmparam->h_parent, GetParent(hCtrl));
//MessageBox(penmparam->h_parent, g_szMsgBuf, g_szMyTitle, MB_ICONERROR | MB_OK);
//****debug<-
}
}

return TRUE;
}

//----------------------------------------------
//name :SetComboSize
//function :コンボボックスのサイズを設定する
// ※ウィンドウ/文字・サイズをn倍に変更しても、ドロップダウン時の
//  サイズを指定しないと文字/ドロップダウンリストは表示されない。
//parameter :hCmb -コンボボックスのウィンドウハンドル
// iLine -ドロップダウン行数
//global :なし
//return :なし

VOID SetComboSize(
HWND hCmb,
INT iLine)
{
RECT rect;
INT w, h;

GetWindowRect(hCmb, &rect);
w = rect.right - rect.left;
h = (INT) SendMessage(hCmb, CB_GETITEMHEIGHT, -1, 0);
h += (INT) SendMessage(hCmb, CB_GETITEMHEIGHT, 0, 0) * iLine;
h += GetSystemMetrics(SM_CYEDGE) * 4;
SetWindowPos(hCmb, NULL, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER);
return;
}

//----------------------------------------------
//name :SetListboxHScrollSize
//function :リストボックスのスクロール幅を設定する
// ※ウィンドウ/文字・サイズをn倍に変更しても、スクロール幅は変わらない。
//parameter :hLsb -リストボックスのウィンドウハンドル
// hFntEmp -使用フォント
//global :[r]g_iLogPixSx
//return :なし

VOID SetListboxHScrollSize(
HWND hLsb,
HFONT hFntEmp)
{
INT n; //行数
INT i; //ループカウンタ
LOGFONT nowfont; //現状のフォント
INT ic = 0; //最長行の文字数
INT iw; //全角文字幅

//拡大後のスクロール幅を求める
n = SendMessage(hLsb, LB_GETCOUNT, 0, 0);
for (i=0; i<n; i++) {
ic = max(ic, SendMessage(hLsb, LB_GETTEXTLEN, i, 0));
}
//拡大後フォントサイズから文字幅を求める
GetObject((HGDIOBJ)hFntEmp, sizeof(LOGFONT), &nowfont);
iw = abs(nowfont.lfHeight) * g_iLogPixSy / 72;
SendMessage(hLsb, LB_SETHORIZONTALEXTENT, iw*ic/2, 0);

InvalidateRect(hLsb, NULL, TRUE);

return;
}

//----------------------------------------------
//name :SetListvwColumnSize
//function :リストビューのカラム幅を設定する
// ※ウィンドウ/文字・サイズをn倍に変更しても、カラム幅は変更されない。
//parameter :hLsv -リストビューのウィンドウハンドル
// hFntEmp -使用フォント
//global :[r]g_iLogPixSy
//return :なし

VOID SetListvwColumnSize(
HWND hLsv,
HFONT hFntEmp)
{

LV_COLUMN lv_clm; //リストビューカラム情報
LOGFONT nowfont; //現状のフォント
TCHAR wokbuf[128]; //ワークバファ
INT i; //ループカウンタ
INT ccnt; //カラム数
INT iw; //全角文字幅
INT ic; //文字数

//現状のフォントサイズを求める
GetObject((HGDIOBJ)hFntEmp, sizeof(LOGFONT), &nowfont);
iw = abs(nowfont.lfHeight);

ccnt = Header_GetItemCount(ListView_GetHeader(hLsv));
for (i=0; i<ccnt; i++) {
lv_clm.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lv_clm.pszText = wokbuf;
lv_clm.cchTextMax = sizeof(wokbuf);
lv_clm.iSubItem = i;
ListView_GetColumn(hLsv, i, &lv_clm);

ic = (strlen(lv_clm.pszText)+1) / 2; //文字数(全角)
lv_clm.cx = (INT)(ic *iw * g_iLogPixSy / 72);
ListView_SetColumn(hLsv, i, &lv_clm);
}

InvalidateRect(hLsv, NULL, TRUE);

return;
}

//----------------------------------------------
//name :ResetScrollInfo
//function :スクロール情報のリセット
//parameter :pSc -スクロール情報テーブル
// iScr_h -スクリーンの高さ
// iMax_y -対象ダイアログの最下部のコントロールy座標
// ※ただし、スクロールされた状態で得られた値
// flExp -元のウィンドウサイズからの拡大率
//global :なし
//return :なし

VOID ResetScrollInfo(
PMYSCROLLINF pSc,
INT iScr_h,
INT iMax_y,
float flExp)
{
INT scrlc; //スクロール移動数

//スクロール関連情報の初期化
pSc->disph = iScr_h - GetSystemMetrics(SM_CYDLGFRAME) * 2;
pSc->dy = (INT)roundf((float)20 * flExp); //元の移動量を20とする
pSc->fullh = max(pSc->disph, iMax_y + pSc->dy);
pSc->range = pSc->fullh - pSc->disph;
pSc->pos = 0;
scrlc = pSc->range / pSc->dy;
if (scrlc <= 0) scrlc = 1;
pSc->boxh = (pSc->disph - GetSystemMetrics(SM_CYVSCROLL) * 2) / scrlc;

return;
}

//----------------------------------------------


ボタンとラベル、エディットボックスくらいまでは、コントロールサイズとフォントサイズを変更するくらいで違和感がなかったのですが、
コンボ、リスト、リストビューは、個別に変更しなければいけないパラメタがありました。(やってみてわかったこと。)

さらに、チェックボックスやラジオボタンは、付随するキャプションはフォントサイズに応じて大きくなるけど、グラフィカルな部分は元のサイズのままです。
なので、個別の描画処理が必要になります。次回サンプルを紹介します。

ダイアログ内をスクロールさせる処理は、Windowsの場合自分で細かい処理を実装しないといけません。
なので、そのための情報を個別に保持するための処理がResetScrollInfo()です。
ローカルなテーブルを構造体として定義しています。

他の定数の定義も含めて、以下の内容を共通のヘッダで定義します。
//----------------------------------------------
#define MY_FONT_FACE "MS Pゴシック"

//RelocationControllers()制御フラグ
#define RELO_INIT 0 //初期化
#define RELO_FONT 1 //フォントのみ再設定
#define RELO_REIN 2 //再初期化

//コントロールの一括拡大処理時の列挙関数へのパラメタ
typedef struct _enmchld_io {
INT proc_type; //処理種別
HWND h_parent; //親(ダイアログ)ハンドル
float exp_rate; //拡縮率
HFONT emp_font; //使用フォント
INT *pbottom; //最大y座標
} ENMCHLD_IO, *PENMCHLD_IO;

//スクロール関係
typedef struct _myscroll_inf {
INT fullh; //本来の表示の高さ
INT disph; //見えている部分の高さ
INT range; //表示されていない部分の高さ
INT pos; //現在の表示縦位置
INT dy; //移動量の単位
INT boxh; //スライダーのサイズ※実は・・・
} MYSCROLLINF, *PMYSCROLLINF;
//----------------------------------------------



細かい処理内容の説明は省きます。
興味のある方は、温かい気持ちでよしなに解釈してやってください。

次回は、ダイアログ/コントロール描画系の続きになります。

では、この辺で。
m(__)m
スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-
本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



PVアクセスランキング にほんブログ村 にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR