びったんびったん

ユーザビリティ・プログラミングについて。

Fit Win の新しいバージョンを公開しました!

f:id:hakomof:20141022011048p:plain

ここでは技術的なことを書きます。 Fit Win の詳細は下記 URL をご覧ください。
Fit Win - ウィンドウを移動・サイズ変更するフリーソフト

開発言語は C#/Slim(HTML5)/Stylus(CSS3)/CoffeeScript(JavaScript) です。ただ本ソフトは Web アプリではありませんし通信もしません。デスクトップアプリなのです。C# のフォーム上に WebBrowser をのっけてその上で HTML5 アプリを動作させています。なぜそんなまわりくどいことをしたのかというと本ソフトがドラッグ&ドロップを多用したり、複雑な描画を行うリッチ GUI アプリだからです。実際 C# のみで書いた過去のバージョンに比べて、本バージョンのコード行数は3分の2に減っています!適切な言語選択の重要性を痛感しましたね。( XAML 使えよってツッコミが入りそうですが使ったことないんですよね。 CSS 並の表現力と jQuery 並の DOM 操作力はあるんでしょうか?)

IE12 で HTML5 たくさん実装されないかなー、でもって Windows 7 にもインストールできないかなー。技術的なことって何書いたらいいのかわからないのでコメントいただければ書くやもです。

最後に本ソフトの開発環境である HTML5/CSS3/JavaScript on C# WebBrowser のメリット・デメリットです。

メリット
  • デスクトップアプリなのに HTML5/CSS3/JavaScript を使ったリッチ GUI アプリを作れる
  • JavaScript でセキュリティ上不可能なことができる
デメリット

マウスカーソルには2つの意味がある

状態を表すマウスカーソル

PC がフリーズしているときの待ちアイコンや、ペイントソフトの鉛筆や消しゴムアイコンのことです。これらのアイコンは PC やソフトの状態に応じて変化し、ユーザがマウスアクションするとどのような効果が起こるのかユーザに伝えます。そのためこれらを適切に設定するとわかりやすくなります。

要素を表すマウスカーソル

マウスポインタがボタンやリンクの上にあると手のアイコンに、テキストエリアの上にあるとキャレットアイコンになります。このような周りと異なるインターフェースを持つ要素の上のマウスカーソルのことです。これらはマウスポインタを要素の上に移動して初めてアイコンがわかります。つまり適当にマウスを振り回してたまたまホバーしたり、リンクの上にたしかに乗っているかを確認するぐらいでしか役に立ちません。

まとめ

  • 状態を表すマウスカーソルは重要なので適切に設定しましょう。
  • 要素を表すマウスカーソルの重要度は少し下がります。むしろボタンなら押したくなるような・ホバーしたくなるようなグラフィックにすることのほうが重要です。

C# で SetWindowsHookEx() を使うと Form がフリーズするのをごり押す

C# でマウスグローバルフックを使うと Form がフリーズします。
c# - SetWindowsHookEx and clicking minimize/maximize/close buttons on form freezing - Stack Overflow

C# から直接呼び出しても、 C++ で DLL を作って呼び出してもフリーズしたので C# でグローバルフックを使えるといっても Form との併用はできないのかと思っていましたがごり押しで動いてしまいました。

やり方は fork もどきをして自身のプロセスを2つ起動します。そして1つ目でフォームを作り、2つ目でグローバルフックをするです。1つ目が2つ目を起動するときに自身のハンドルをコマンドライン引数で渡して、2つ目が1つ目と通信したいときにはそのハンドルに PostMessage() します。片方が死んだときにもう片方を殺すのを忘れないでください。

あまりにごり押しなのでコードは載せません。

Undo の設計 - 元に戻す・戻さない操作の違い

例えばテキストエディタに文字を入力してから元に戻すと、入力した文字は消える。しかしテキストエディタでスクロールしてから元に戻しても、スクロールは戻らない。当たり前かな?じゃあコードの折りたたみはどうだろう?答えはどちらもで、 Visual Studio は元に戻るし、 Sublime Text は元に戻らない。つまり Sublime Text でコードを折りたたんだ後で折りたたみを解除しようとしたら、その直前に入力した文字が消えてあわててやり直す( Ctrl + Y )ことになる。大変よくない。

これはほんの一例だが元に戻す操作と元に戻さない操作の違いは不明瞭だ。この辺りをネットで調べようと思っても Undo の実装しか引っかからない(それはそれで大事なのだが)。仕方がないので自分で書く。なのでこの辺りに詳しいサイトや書籍をご存知でしたら教えてください。

元に戻す操作と元に戻さない操作の違いが明瞭になると何が嬉しいのか?

ユーザの予想が効く

文字の入力は元に戻りそうだから Ctrl + Z 、スクロールは元に戻らなさそうだから逆スクロール、のような予想の効く操作が増え、とりあえず Ctrl + Z してみて駄目だったら Ctrl + Y するような残念な UI が減る。

Undo は PC 初心者でもわりと使うショートカットなのでユーザ全体の使い勝手が向上する。馬鹿にできない。

開発が楽になる

昨今のリッチなソフトウェアにおいて Undo 機能は必須だが売りにはできない空気のようなものだ。そんなもののために元に戻すか戻さないかで悩まなくてすむ。

あれ?スマホアプリや Web サービス全盛の時代にこの話関係なくない?スマホアプリは関係ないかもしれない。しかし遠い将来、少なくない数の Web サービスが PC Twitter クライアントのようにブラウザ上ではなく OS 上で動くかたちになっているはずだ(なっていてほしい)。Web サービスを使うにはブラウザをアクティブにしてからタブを切り替えるなど2度手間だし、ブラウザに奪われてショートカットも満足に使えないし、うんざりである。Web サービスはどの OS でもどのスマホでも動くクロスプラットフォームだからって安易に量産されすぎだ!開発者の怠慢だ!

元に戻す操作と元に戻さない操作のパターン

ファイルの内容が変更される操作を元に戻せるようにする

先の例だがテキストエディタに文字を入力するとファイルの内容が変更される。しかしスクロールしても見え方(ソフトウェアの状態)が変わるだけでファイルの内容は変更されない。このパターンに従うとコードの折りたたみは元に戻らないことになるが、私は元に戻るほうが便利だと思う。

元に戻すコストが大きい操作もしくは不可逆な操作を元に戻せるようにする

文字列を Backspace 押しっぱなしでガーッと消した後に Ctrl + Z なしに元に戻すのは手間である。またコードの折りたたみも押しやすいショートカットが割り当てられていることは少なく元に戻しづらい。だから元に戻せるようにする。しかし Visual Studio の出力のクリアなどは元に戻せなかったりする。

メインコンテキスト上の操作を元に戻せるようにする

Visual Studio の出力のクリアなどを元に戻せるようにするとひとつ困ったことが起こる。それはエディタ上で Undo するとエディタが元に戻り、出力上で Undo すると出力が元に戻るため、エディタを元に戻したいのに出力上で Undo してしまうことだ。そのためメインコンテキストであるエディタだけ元に戻せるようにし一貫性を保つことでユーザが混乱しなくてすむようにする。しかし別のコンテキスト上のインプットボックスも元に戻せたりする。

ユーザが元に戻したいと思う操作を元に戻せるようにする

ここまで譲歩して都合のよい解釈をしても駄目だ。 Visual Studio の定義へ移動という機能があるがこれは Ctrl + Z では元に戻らない。しかしページ遷移専用の元に戻るとも言うべき前に戻る( Ctrl + - )で元に戻る。つまり Ctrl + Z には一定の基準がありそれにあぶれつつも元に戻したい操作には別の元に戻るコマンドを新たに作らないといけないらしい。

すべての操作を元に戻せるようにする

Ctrl + Z を連打することになってしまう。

まとめ

複雑すぎてまとめきれなかった。元に戻す操作か元に戻さない操作か迷っている方の参考なれば嬉しいです。

Windows 10 未満のコマンドプロンプトを Ctrl + V で貼り付け

Windows 10 でコマンドプロンプトが使いやすくなるみたいですね。 Windows 10 ほどではありませんが既存の Windowsコマンドプロンプトを Ctrl + V で貼り付けができるようにし、少しだけ使いやすくする方法を紹介します。

汎用キーバインディング変更ソフトである yamy を使います。

Yet Another Mado tsukai no Yuutsu プロジェクト日本語トップページ - SourceForge.JP
こちらのページからダウンロードして解凍します。インストールは不要ですので yamy.exe を実行します。

次に設定ファイルを作成して、拡張子を .mayu とする適当な名前で保存します。 # 以降の行はコメントになるので、お使いのキーボードに合わせて1行目・2行目いずれかをコメントアウトしてください。

include '109.mayu' # 日本語キーボード
# include '104.mayu' # 英語キーボード

window CommandPrompt /ConsoleWindowClass/ : Global

key C-V = A-Space E P

最後にタスクトレイにある yamy アイコンを右クリックして設定・追加・参照の順にクリックして先ほど作成した設定ファイルを指定します。これで設定は終了です。

実際にコマンドプロンプトを起動して Ctrl + V してみると貼り付けができると思います。

yamy は応用が効き様々なことができるのでおすすめです。

Form.Close() メソッドの CloseReason は UserClosing

Form アプリケーションがどのように終了する(した)のかは CloseReason で知ることができる。
CloseReason 列挙体 (System.Windows.Forms)

これの注意点として CloseReason が UserClosing になるのはウィンドウの閉じるボタンや Alt + F4 で終了するときだけではなく、 Form.Close() メソッドを呼んだときもである。

ShowWindow(hWnd, SW_RESTORE) は Visible プロパティを更新しない

やりたいこと

最小化されていてかつ非表示なフォームがある。これを表示し元のサイズに戻したい。

バグ

状況を再現する最小のコードを書いた。元のサイズに戻すことは .NET ではできないので Win32 の ShowWindow(hWnd, SW_RESTORE) 関数を使う。

class C {

    [DllImport("user32")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_RESTORE = 9;

    private void F() {
        var f = new Form {
            WindowState = FormWindowState.Minimized
        };
        ShowWindow(f.Handle, SW_RESTORE);

        Console.WriteLine(f.Visible); // フォームは表示されているのになぜか Visible は false
    }
}

フォームは表示されているのになぜか Visible は false になる。イミワカンナイ

なにがまずいのか

.NET がフォームをごにょごにょするときに Visible を参照して表示か非表示かを判断していることがありそこがバグる

具体的にはフォームに関連付けられたコンテキストメニューは、フォームが表示されているときにしか表示できないらしい。先のバグでフォームが表示されているにもかかわらずコンテキストメニューが表示できなかった。

じゃあどうすんの

ShowWindow(f.Handle, SW_RESTORE);
f.Show();
// f.Visible = true;

ShowWindow 関数の直後に Show メソッドを呼ぶか Visible プロパティに true をセットする。かなり気持ち悪いが、今のところ不具合は起きていない。もっとよい方法があれば教えてください。

まとめ

.NET と Win32 が混在するときには最新の注意を払いましょう。