ボタンの hover 切り替えのための画像を作成するツール

チープなツールシリーズ第1弾ッ!!! ということで(多分、第1弾でおしまい。単なるアクセス解析のためのブログ更新だし)。

マウスをホバーさせる時の画像の作り方/スクリプトの書き方は色々あるわけですが、CSS を使って、

hoverでリンク画像を切り替える – スタイルシートTIPS ふぁくとりー
http://www.nishishi.com/css/link-image-hoverchange.html

な方法が使えます。

1
2
3
4
5
6
7
8
9
10
11
.menu#menu1 a {
    display: block;
    width: 160px;
    height: 60px;
    text-indent: -4000px;
    background-image: url(./image/menu1.png);
}
.menu a:hover {
    text-decoration: none;
    background-position: top right;
}

昔は JavaScript で切り替えたのですが、CSS だとこれが便利かなと。ホバー前の画像とホバー時の画像がひとつにまとまっているのでロード時に読み込まれるのがいいのです。ホバー時に読み込むと、読み込みが遅かったりしてなかなか画像がでなかったりするので。
で、このホバー用の画像、photoshop とかを持っていると作るのは簡単なんでしょうが(多分、Blend でも良いかと)、適当な画像ツールがない私としてはひと苦労で、大抵は Excel のワードアートを使っているという始末。

これを横に並べるのが面倒…なので、それ専用のツールを作りました。

構想はお昼の散歩時に15分ぐらいで、製作は30分ぐらい。前後合わせると1時間ちょっとというところでしょうか。この手のツールは、画像加工をするときのイライラ度合を考えると、手元で作っていたほうが精神的によいのです。
で、ソースの全文はこんな感じ。約100行位。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
namespace MakeMenuImage
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private Bitmap bmpNormal;
        private Bitmap bmpHover;
        private Bitmap bmpMenu;
 
        private void Form1_Load(object sender, EventArgs e)
        {
            Properties.Settings.Default.Reload();
            textLeft.Text = Properties.Settings.Default.ImageLeft.ToString();
            textTop.Text = Properties.Settings.Default.ImageTop.ToString();
            textWidth.Text = Properties.Settings.Default.ImageWidth.ToString();
            textHeight.Text = Properties.Settings.Default.ImageHeight.ToString();
        }
 
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Properties.Settings.Default.ImageLeft = int.Parse(textLeft.Text);
            Properties.Settings.Default.ImageTop = int.Parse(textTop.Text);
            Properties.Settings.Default.ImageWidth = int.Parse(textWidth.Text);
            Properties.Settings.Default.ImageHeight = int.Parse(textHeight.Text);
            Properties.Settings.Default.Save();
        }
 
        private void btnNormal_Click(object sender, EventArgs e)
        {
            bmpNormal = new Bitmap(Clipboard.GetImage());
            picNormal.Image = bmpNormal;
        }
 
        private void btnHover_Click(object sender, EventArgs e)
        {
            bmpHover = new Bitmap(Clipboard.GetImage());
            picHover.Image = bmpHover;
        }
 
        private void btnToClip_Click(object sender, EventArgs e)
        {
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));
 
            bmpMenu = new Bitmap(rect.Width*2, rect.Height);
            Graphics g = Graphics.FromImage(bmpMenu);
            g.FillRectangle(Brushes.White, new Rectangle(0, 0, rect.Width * 2, rect.Height));
            g.DrawImage(bmpNormal,
                new Rectangle(0, 0, rect.Width, rect.Height),
                rect,
                GraphicsUnit.Pixel);
 
            g.DrawImage(bmpHover,
                new Rectangle(rect.Width,0, rect.Width, rect.Height),
                rect,
                GraphicsUnit.Pixel);
            Clipboard.SetImage(bmpMenu);
 
            picMenu_MouseLeave(sender, e);
        }
 
        private void picMenu_MouseEnter(object sender, EventArgs e)
        {
            if (bmpMenu == null) return;
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));
            Bitmap bmp = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(bmpMenu,
                new Rectangle(0, 0, rect.Width, rect.Height),
                new Rectangle(rect.Width, 0, rect.Width, rect.Height),
                GraphicsUnit.Pixel);
            picMenu.Image = bmp;
        }
 
        private void picMenu_MouseLeave(object sender, EventArgs e)
        {
            if (bmpMenu == null) return;
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));
            Bitmap bmp = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(bmpMenu,
                new Rectangle(0, 0, rect.Width, rect.Height),
                new Rectangle(0, 0, rect.Width, rect.Height),
                GraphicsUnit.Pixel);
            picMenu.Image = bmp;
        }
    }
}

本来ならば、画像ファイルの保存や、Excel との連携、ドラッグアンドドロップなどを考えたりするのですが、そんなのは面倒なのでクリップボード経由で Clipboard.GetImage() で画像を取り込んでいきます。保存先もクリップボードなので、IrfanView を使うかペイントに貼りつけます。

MouseEnter/MouseLeave イベントのところは、試しにマウスをhoverさせて確認できるようにしてあるところですね。このところ、hover されるたびに new Bitmap をしているので、hover するたびにメモリががんがん増えるという…苦笑レベルなのですが、まあツールなので良しとしましょう。本来ならば、Bitmap オブジェクトを使い廻すようにすればよいのです。

さて、これを作っているときに思ったのですが、この操作は metro アプリでは苦手な部類になります。
手順を簡単に書いてみると、

  1. Excel で通常画像とホバー画像の二種類の画像を作成する。
  2. Excel で通常画像を選択して、クリップボードへコピー
  3. 画像加工ツールを起動して、「Set Normal」ボタンをクリックしてペースト
  4. Excel に戻って、ホバー画像を選択して、クリップボードへコピー
  5. 画像ツールに戻って、「Set Hover」ボタンをクリックしてペースト
  6. 「ToClip」ボタンを押して、加工画像をクリップボードへ
  7. ペイントに、クリップボードがから画像をペースト
  8. ペイントで、ファイル名を付けて保存する。

という具合。ユースケースから見ると、Excel、画像ツール、ペイントを行き来します。
metro アプリの場合、新しいアプリを起動させてしまうと元のアプリに戻るのが結構面倒です。左からタスクを出して元のアプリを選択して、って具合になります。デスクトップアプリだと複数の Window を並べておいてクリックします。あるいは、Ctrl+Tab で移動します。まぁ Ctrl+Tab 自体は metro でも有効なのですが、「Window を並べておいて、mouse でクリックする」という動作はできないわけです。

この不便さは、実は iPhone/iPad にもあって、twitter アプリでブラウザを起動すると元に戻ることが面倒なので、アプリ内ブラウザで凌いでいます。本来ならばメールアプリやその他のツールと行き来できたほうがいいのですが、以前はクリップボードが無かった時代があったり、シングルタスクであったりしたものですから、このあたりの連携は微妙なところがあります。

windows 8 の metro app の場合は「プロトコル」ということで「mailto://」みたいなのが自由に作成できます。ただし、このプロトコルや通知は、事前にデータ形式を決めておかないと駄目という微妙なところがあって、なんか妙なところでハマりそうなんですよねぇ。どうなんですかね?

クリップボードの場合は、複数の形式のデータを同時に置くことができるので、例えば Excel の場合は、

  • テキストデータ
  • HTML形式のデータ
  • リッチテキスト形式のデータ
  • 画像データ
  • Excel 独自の書式付きのデータ

などが、いちどにクリップボードに送られます。で、クリップボードからデータを取り出す側で、適当な形式のデータのみ取り出せるという方式になっているのです。metro app の通知の場合には、どうなんでしょう。共有コントラクトあたりを使うか、それとも通知だけプロトコルを使って後はクリップボード経由とか?

「コントラクト」でMetroスタイル・アプリのサンドボックスを乗り越える! - @IT
http://www.atmarkit.co.jp/fdotnet/chushin/readyforwin8app_02/readyforwin8app_02_02.html

カテゴリー: ツール パーマリンク