HSP3 あれこれ <ウィンドウ>

Last Update : 2007/04/xx

XREA.COMのs206サーバー内のサイトは、Yahoo!検索からペナルティを受け、検索結果で常に下位にランク付けされてしまうので、Yahoo!経由向けに用意したHSPミニ講座内のページです。このページは「http://lhsp.s206.xrea.com/hsp_window.html」の昔のものです。

ウィンドウスタイルの変更

HSPのウィンドウID 0 は、最大化ボタンとサイズ可変が無効になっています。ウィンドウスタイルを変更して、利用できるようにしてみます。ちなみに、スクリプトからウィンドウを最大化、最小化するには、sendmsg命令を利用。

;	最大化ウィンドウ、ウィンドウサイズ可変サンプル (by Kpan)

#uselib "user32"
#cfunc GetWindowLong "GetWindowLongA" int, int
#func SetWindowLong "SetWindowLongA" int, int, int

;	あらかじめ最大サイズでウィンドウを作成
	screen 0, ginfo (20), ginfo (21)

;	自ウィンドウのスタイルを変更。
;	$10000 = 最大化ボタン有効
;	$40000 = ウィンドウサイズ可変有効
	SetWindowLong hwnd, -16, GetWindowLong (hwnd, -16) | $10000 | $40000

;	width命令でウィンドウサイズ指定。再描画の意味合いもある。
	width 200, 200

;	最大化ウィンドウにしたい場合
;	sendmsg hwnd, $112, $F030

今度は上の逆パターン、すでに設定されているウィンドウスタイルを取り除きます。

#uselib "user32"
#cfunc GetWindowLong "GetWindowLongA" int, int
#func SetWindowLong "SetWindowLongA" int, int, int

;	-$20000 = 最小化/最大化ボタンなしウィンドウ
;	-$80000 = システムメニューなしウィンドウ
;	-$400000 = ウィンドウ境界線なしウィンドウ
;	-$800000 = タイトルバーなしウィンドウ
	SetWindowLong hwnd, -16, GetWindowLong (hwnd, -16) - $80000

	width 400, 200

;	システムメニューを外すと終了ボタンが消えてしまうのでキー押し終了用
	onkey *exit

	stop

*exit
	end

最後に拡張ウィンドウスタイルの変更。

#uselib "user32"
#cfunc GetWindowLong "GetWindowLongA" int, int
#func SetWindowLong "SetWindowLongA" int, int, int

;	+$1000 = タイトル名部分が右側に位置するウィンドウ
;	+$20000 = 境界枠なし(細枠)ウィンドウ
	SetWindowLong hwnd, -20, GetWindowLong (hwnd, -20) | $1000

	width 400, 200

^

ウィンドウ背景色

HSPのウィンドウ背景はデフォルトで白色です。しかし、一般的なツール系プログラムの背景色は、灰色表示(XPスタイルの場合は白に近い灰色)。HSP2では、背景色にcls命令を利用していた人がいるかもしれませんが、オブジェクト(分かりやすいのがchkbox命令)の表面色と違っていたと思います。

HSP3ではシステムカラーを標準命令で取得できるようになりました。syscolor命令のインデックス番号15がウィンドウ表面の色になります。(→ 参照、システムカラー一覧チェックツール)

※注意! Windows 9x系OS、かつ画面が16ビット(High Color)の場合は色がずれます。。。(*_*)

;	オブジェクトの表面色とは異なるcls命令
;	cls 1

	syscolor 15
	boxf

	pos 100, 100
	chkbox "AAAA", a

;	mes命令、line命令など描画命令を使う場合はcolor命令で色を戻す
	color
	mes "あいうえお"

ところで、Windows XP環境は、「Luna」(ルナ)という新たなユーザーインターフェイスが採用され、オブジェクトのデザインが変更されました。その1つにタブコントロールの表面色があり、よく見ると分かりますが、クラシックスタイル時の統一された1つの色ではなく、上下にかけて3段階のグラデーションがかかっていると思います。この表面色を表示してみることにします。

当然ながら、Windows XP環境でXPスタイルが有効になっていないといけません。サンプル上では、XP環境以外(=XPスタイルの処理を担当するuxtheme.dllがシステムに含まれていない)の人や、クラシックスタイルに設定しているXP環境の人には、従来の表示になるようにしてあります。

;	XP/Vistaスタイルのグラデーション表面色表示モジュール (by Kpan)

#module

#uselib "uxtheme"
#cfunc IsThemeActive "IsThemeActive"
#cfunc OpenThemeData "OpenThemeData" int, wstr
#func DrawThemeBackground "DrawThemeBackground" int, int, int, int, int, int
#func CloseThemeData "CloseThemeData" int

;	isuxtheme () 関数
;	uxtheme.dllが存在するか、XP/Vistaスタイルが有効かを確認。
;	必ず一回呼んで確認してください。1が返ると有効、0が返ると無効。
#defcfunc isuxtheme
	exist dirinfo (3) + "\\uxtheme.dll"
	if strsize != -1 {
		if IsThemeActive () : return 1
	}
	return 0

;	uxbgcolor p1, p2, p3, p4
;	p1〜p4=左上XY、右下XYの座標位置
;	Windows XP/Vista環境以外のPCでは呼ばないでください。
#deffunc uxbgcolor int p1, int p2, int p3, int p4
	hTheme = OpenThemeData (hwnd, "tab")

	dim RECT, 4
	RECT = p1, p2, p3, p4
	DrawThemeBackground hTheme, hdc, 10, , varptr (RECT)

	CloseThemeData hTheme

	return

#global

;	以下、サンプル

;	XP/Vistaスタイルが利用可能かを確認。いきなりxpbgcolor命令を
;	呼ぶと、Windows XP環境以外のPCではエラーが出て落ちます。
;	OSごとに処理を振り分ける形にしてください。
	stylecheck = isuxtheme ()

	if stylecheck {
;		有効(1)なら、ウィンドウ全体をグラデーション色に
		uxbgcolor 0, 0, ginfo (6), ginfo (7)
		redraw ; 要再描画
	} else {
;		無効なら、普通にクラシック灰色表示に
		syscolor 15 : boxf
	}

^

ウィンドウリージョン

ウィンドウにおけるリージョンオブジェクトの作成です。四角形ウィンドウのところをあらかじめ指定した形状に変更します。丸いウィンドウや三角形ウィンドウなどが実現できます。ちょくとさんのページを参照。

マウスでウィンドウを移動できるようにするには、sendmsg命令など使います。もしbgscr命令でウィンドウを作成した場合は、システムメニューがなくなってしまうので、何かしらのプログラム終了処理を用意しておく必要があります。

まずは円形。CreateEllipticRgn関数を使用します。

;	リージョンサンプル (by Kpan)

#uselib "gdi32" 
#cfunc CreateEllipticRgn "CreateEllipticRgn" int, int, int, int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	screen , 300, 200
	cls 3

;	四角形の左上XYと右下XYの座標を指定、
;	この座標で作成された四角形内に隣接する円が描かれます。
	hRegion = CreateEllipticRgn (0, 0, 300, 200)

;	それをウィンドウに適用
	SetWindowRgn hwnd, hRegion, 1

次に、CreateRoundRectRgn関数。これは角の丸い四角形を作成です。

;	リージョンサンプル (by Kpan)

#uselib "gdi32" 
#cfunc CreateRoundRectRgn "CreateRoundRectRgn" int, int, int, int, int, int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	screen , 300, 200
	cls 2

;	四角形の左上XYと右下XYの座標、
;	p5とp6は角の丸み楕円部分の高さと幅です。
	hRegion = CreateRoundRectRgn (0, 0, 300, 200, 50, 100)

	SetWindowRgn hwnd, hRegion, 1

今度は、CreatePolygonRgn関数。多角形リージョンです。

;	リージョンサンプル (by Kpan)

#uselib "gdi32" 
#cfunc CreatePolygonRgn "CreatePolygonRgn" int, int, int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	screen , 400, 400
	cls 2

;	例として三角形。この場合だと、3つの頂点ののXY座標
	POINT = 200, 0, 0, 400, 400, 400

;	p2は頂点の数。
	hRegion = CreatePolygonRgn (varptr (POINT), 3, 1)

	SetWindowRgn hwnd, hRegion, 1

CombineRgn関数。これはリージョンとリージョンの結合を行います。

;	リージョンサンプル (by Kpan)

#uselib "gdi32"
#cfunc CreateRectRgn "CreateRectRgn" int, int, int, int
#cfunc CreateEllipticRgn "CreateEllipticRgn" int, int, int, int
#func CombineRgn "CombineRgn" int, int, int, int
#func DeleteObject "DeleteObject" int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	screen , 270, 300
	cls 1

;	現在のウィンドウ(四角形)のリージョン作成。
	hRegion.0 = CreateRectRgn ( , , ginfo (6), ginfo (7))

;	大きい円のリージョン作成
	hRegion.1 = CreateEllipticRgn (80, 90, 210, 220)

;	四角形からこの円形部分のみ取り出す。
	CombineRgn hRegion.0, hRegion.0, hRegion.1, 4

;	使われないhRegion.1は削除
	DeleteObject hRegion.1

;	小さい円のリージョンを作成
	hRegion.1 = CreateEllipticRgn (20, 30, 270, 280)

;	大きい円からこの小さい円部分を取っ払う
	CombineRgn hRegion.0, hRegion.0, hRegion.1, 1

;	使われないhRegion.1は削除
	DeleteObject hRegion.1

;	ドーナツ型ウィンドウになる
	SetWindowRgn hwnd, hRegion.0, 1
;	リージョンサンプル (by Kpan)

#uselib "gdi32"
#cfunc CreateEllipticRgn "CreateEllipticRgn" int, int, int, int
#func CombineRgn "CombineRgn" int, int, int, int
#func DeleteObject "DeleteObject" int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	screen , 350, 350
	cls 2

;	1つ目の円、2つ目の円
	hRegion.0 = CreateEllipticRgn (0, 0, 200, 100)
	hRegion.1 = CreateEllipticRgn (0, 20, 350, 350)

;	結合。第4パラには結合方法(1〜4)。重なり方が変わります
	CombineRgn hRegion.0, hRegion.0, hRegion.1, 3

	DeleteObject hRegion.1

	SetWindowRgn hwnd, hRegion.0, 1

文字列からリージョンデータを起こしてみる。

;	リージョンサンプル (by Kpan)

#uselib "gdi32"
#func BeginPath "BeginPath" int
#func EndPath "EndPath" int
#cfunc PathToRegion "PathToRegion" int

#uselib "user32"
#func SetWindowRgn "SetWindowRgn" int, int, int

	font "MS Pゴシック", 80, 1
	color $FF, $FF : boxf

	BeginPath hdc
		mes "□▽△◎○!"
	EndPath hdc

	SetWindowRgn hwnd, PathToRegion (hdc), 1
	stop

^

移動できないウィンドウ

タイトルバーをドラッグしても移動できないウィンドウを作成するには、システムメニューの[移動]項目を削除することで実現できます。

#uselib "user32"
#cfunc GetSystemMenu "GetSystemMenu" int, nullptr
#func DeleteMenu "DeleteMenu" int, int, nullptr

;	システムメニューのハンドル取得
	hSystemMenu = GetSystemMenu (hwnd)

;	第2パラに削除する項目。
;	$F000=サイズ変更、$F010=移動、$F020=最小化、$F030=最大化、
;	$F060=終了、$F120=元のサイズに戻す
	DeleteMenu hSystemMenu, $F010

^

ウィンドウの表示アニメート

ウィンドウ表示のアニメーションができます。この AnimateWindow関数 はWindows 98/2k以降の対応です。第2パラは表示時間(ミリ秒)、第3パラは アニメーションのタイプ。

;	ウィンドウアニメーションサンプル (by Kpan)
;	!要Windows 98/2k以降

#uselib "user32"
#func AnimateWindow "AnimateWindow" int, int, int

#define AW_SLIDE $40000
#define AW_ACTIVATE $20000
#define AW_BLEND $80000
#define AW_HIDE $10000
#define AW_CENTER $10
#define AW_HOR_POSITIVE $1
#define AW_HOR_NEGATIVE $2
#define AW_VER_POSITIVE $4
#define AW_VER_NEGATIVE $8

	objsize 200
	button "スライドアニメート", *slide
	button "中央に向かってアニメート", *center
	button "フェードアウト/フェードイン", *fade
	
	stop

;	スライドアニメート
*slide
	AnimateWindow hwnd, 2000, AW_HOR_POSITIVE | AW_HIDE ; 左→右
	AnimateWindow hwnd, 2000, AW_HOR_NEGATIVE | AW_ACTIVATE ; 右→左
	AnimateWindow hwnd, 2000, AW_VER_POSITIVE | AW_HIDE ; 上→下
	AnimateWindow hwnd, 2000, AW_VER_NEGATIVE | AW_ACTIVATE ; 下→上
	stop

;	中央に向かってアニメート
*center
	AnimateWindow hwnd, 2000, AW_CENTER | AW_HIDE ; 中央に
	AnimateWindow hwnd, 2000, AW_CENTER | AW_ACTIVATE ; 元に戻る
	stop

;	ウィンドウのフェードアウト/フェードイン
*fade
	AnimateWindow hwnd, 1000, AW_BLEND | AW_HIDE ; フェードアウト
	AnimateWindow hwnd, 1000, AW_BLEND | AW_ACTIVATE ; フェードイン
	stop

^

ウィンドウの非アクティブ化チェック
;	WM_ACTIVATE (ウィンドウがアクティブ/非アクティブになった時)
	oncmd *jump, $6

	stop

*jump
	check = wparam & $FFFF

;	0=非アクティブ化 /
;	1=クリック以外でアクティブ化 / 2=クリックでアクティブ化
	mes ""+check+""

^

ウィンドウの無効化

EnableWindow関数でウィンドウ全体の無効化、有効化を切り替えできます。オブジェクト単体もこの関数を利用することで、有効無効の切り替えが可能です。(→参照、HSP3あれこれ チェックボックスの監視

#uselib "user32"
#func EnableWindow "EnableWindow" int, int

;	無効化。第1パラにウィンドウハンドル。(HSPのシステム変数hwnd)
	EnableWindow hwnd

	listbox a, 50, "HSP\nサンプル\nウィンドウ"
	chkbox "あいう", b

	wait 300

	mes "解除"

;	第2パラで状態指定。1=有効化、0=無効化
	EnableWindow hwnd, 1

^

ウィンドウの最大化・最小化判定

このページで紹介しているように、ウィンドウスタイルを変更することで最大化ウィンドウが実現可能です。さて、ウィンドウが最大化の状態か、最小化(アイコン化)の状態か、普通の状態かの判定。それぞれの関数で1が返ってきた場合に、最大化・最小化となります。

#uselib "user32"
#func SetWindowLong "SetWindowLongA" int, int, int
#cfunc GetWindowLong "GetWindowLongA" int, int
#cfunc IsIconic "IsIconic" int
#cfunc IsZoomed "IsZoomed" int

	screen , ginfo (20), ginfo (21)
	SetWindowLong hwnd, -16, GetWindowLong (hwnd, -16) | $10000 | $40000

	onexit gosub *exit

	width 300, 300

	stop

*exit
;	第1パラにウィンドウハンドル値を指定
;	(もし複数のウィンドウIDがあるならば、ハンドル値をあらかじめ別変数に入れておく)
	dialog "最小化: "+IsIconic (hwnd)+"\n最大化: "+IsZoomed (hwnd)+""
	end

^

ウィンドウの半透明化

SetLayeredWindowAttributes関数を利用したウィンドウの半透明処理です。この関数は、Windows 2000/XP/Vistaで対応しています。透過時は通常よりもPCスペックが要求されます。

簡単に流れを書くと、現在の拡張ウィンドウスタイルに WS_EX_LAYERED を追加して、SetLayeredWindowAttributes関数で透過状態にします。下のサンプルでは、トラックバー(参照)で透明度をリアルタイムで調節できる処理にしてあります。

;	ウィンドウの半透明化
;	要 Windows 2k 以降

#uselib "user32"
#cfunc GetWindowLong "GetWindowLongA" int, int
#func SetWindowLong "SetWindowLongA" int, int, int
#func SetLayeredWindowAttributes "SetLayeredWindowAttributes" int, int, int, int

#define WS_EX_LAYERED $80000

#define LWA_COLORKEY 1
#define ULW_ALPHA 2

;	簡易OS確認
	if varptr (SetLayeredWindowAttributes) = 0 {
		dialog "Windows 2k/XP/Vista じゃないっぽい。。。"
		end
	}

;	トラックバー用メッセージ WM_VSCROLL
	oncmd gosub *vscroll, $114

;	トラックバー (透明度調節)
	pos 200, 200
	winobj "msctls_trackbar32", "", , $50000000 | $100, 200, 30
	hTrackbar = objinfo (stat, 2)
	sendmsg hTrackbar, $405, 1, 100

;	拡張ウィンドウスタイルにWS_EX_LAYEREDを追加適用
	SetWindowLong hwnd, -20, GetWindowLong (hwnd, -20) | WS_EX_LAYERED

	stop


*vscroll
	if lparam = hTrackbar {
		sendmsg hTrackbar, $400
;		第3パラに透明度 (アルファ値の範囲 0〜255)
		bAlpha = 255 * stat / 100
		SetLayeredWindowAttributes hwnd, , bAlpha, ULW_ALPHA
	}

	return

^

タイトルバーの点滅

点滅は1回。複数回点滅させる場合は repeat命令 などを使用???

#uselib "user32"
#func FlashWindow "FlashWindow" int, int

	wait 100
	FlashWindow hwnd, 1

^

タイトルバーのアイコン変更

タイトルバー部分のアイコンをWindowsが持っているアイコン(詳細はちょくとさんのページ参照)に変更します。以下はすべてのウィンドウでアイコンが変わります。

;	タイトルバーアイコン変更サンプル1 (by Kpan)

#uselib "user32"
#cfunc LoadIcon "LoadIconA" int, int
#func SetClassLong "SetClassLongA" int, int, int

	screen 2

;	LoadIcon関数の第1パラにアイコンID。
;	「32513」=「停止マーク」。
	SetClassLong hwnd, -14, LoadIcon ( , 32513)

;	元に戻すには
	wait 200
	SetClassLong hwnd, -14, LoadIcon (hinstance, 128)

今度は個別ウィンドウごとの変更です。

;	タイトルバーアイコン変更サンプル2 (by Kpan)

#uselib "user32"
#cfunc LoadIcon "LoadIconA" int, int

	screen 2

;	LoadIcon関数の第1パラにアイコンID。
;	「32515」=「!マーク」。
	sendmsg hwnd, $80, 1, LoadIcon ( , 32515)

	screen 3

;	「32514」=「?マーク」
	sendmsg hwnd, $80, 1, LoadIcon ( , 32514)

;	元に戻すには
	wait 200
	sendmsg hwnd, $80, 1, LoadIcon (hinstance, 128)

^

Copyright © Kpan. All rights reserved.