[AutoHotKey]ドロップダウンリストの内容に合わせて横幅を決める


ドロップダウンリストの内容が規定の幅を超えたら内容が全く見えねえじゃねえか問題。

リストに入りうる文字列長が予めわかっていればどうとでもなるが、そうでないなら文字列から長さを計算する必要がある。

計算する上での問題

1.ドロップダウンリストの内容は単一の文字列である

画像の例でいえば、ドロップダウンリストを追加するコマンドは以下のようになっている。

SpellCaster.ahk
gui, add, DropDownList, , 黄昏よりも昏きもの 血の流れより紅きもの 時の流れに埋もれし 偉大なる汝の名において 我ここに 闇に誓わん 我等が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて 等しく滅びを与えんことを!||闇よりもなお昏きもの 夜よりもなお深きもの 混沌の海にたゆたいし 金色なりし闇の王 我ここに汝に願う 我ここに汝に誓う 我が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて等しく滅びを与えんことを!

全ての要素を合わせて(デリミタを挟んだ)ひとつの文字列になってしまう。よって、まず各要素の文字列長からして素直に取れない。

2. 文字列長は実際の幅ではない

当たり前だが、文字列長は文字数であって物理的?な長さではない。等幅フォントならフォントサイズから計算できるかも知れないが、全角半角が混じるとめんどいというレベルじゃないし、プロポーショナルフォントならどうしようもない。

問題の解決

1. 各要素を配列にも格納する

やり方はまあどこから要素を持ってくるかによるが、まずは各要素をバラバラに扱うために配列に格納するのがはじまりだろう。

別にドロップダウンリストの文字列をStringSplitするんでもいいっちゃいいが、初期選択項目を決めるためにデリミタがひとつだけ二重になってたりしてめんどい。疑似配列の扱いもちょっとめんどいし。

SpellCaster.ahk
; 呪文を配列に格納
Spells := Object(1,"黄昏よりも昏きもの 血の流れより紅きもの 時の流れに埋もれし 偉大なる汝の名において 我ここに 闇に誓わん 我等が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて 等しく滅びを与えんことを!",2,"闇よりもなお昏きもの 夜よりもなお深きもの 混沌の海にたゆたいし 金色なりし闇の王 我ここに汝に願う 我ここに汝に誓う 我が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて等しく滅びを与えんことを!")

for, index, Spell in Spells
{
    If (A_Index = 1)
    {
        SpellsV .= Spell "|" ; 1つ目の呪文を初期選択項目とする
        LongSpell := Spell
    }
    Else
    {
        SpellsV .= "|" Spell
        If (StrLen(Spell) > StrLen(LongSpell)) ; 現在のLongSpellより長ければLongSpellに代入
            LongSpell :=  Spell
    }
}

gui, add, DropDownList, , %SpellsV%

このように、一度配列に格納すれば、各要素のうちもっとも文字列長の長い要素を得ることができる。

2. Textコントロールを使い、文字列から実際の長さを得る

文字列から実際の長さを得るには、なにかに文字列を入れて測ってみればよい。

ここで、物差しに使うのは、文字列に合わせて拡縮してくれるものでなければいけない。

Wn | 省略時は、一部のコントロールはText引数の内容が収まるように自動設定され、それ以外の場合はフォントサイズの15倍に設定される。

Gui,Add - AutoHotkey Wiki

これはまあ、普通にTextコントロールでやるのがよかろう。
コントロールの幅は、GuiControlGetで得ることができる。

スクリプト上で長さを得るのに、実際にGuiを表示する必要はない。
Hiddenオプションで非表示のコントロールとしてもよいし、Showする前にDestoroyして、それからドロップダウンリスト等を改めてAddしてもよい。

SpellCaster.ahk
; 最長呪文の表示に必要な幅を取得
Gui, Add, Text, vSizer, %LongSpell%
GuiControlGet, LongSpellLen, Pos, Sizer
Gui, Destroy

実装

両方合わせたのが以下のコード。

ドラグスレイブとギガスレイブから選択して詠唱できます。

SpellCaster.ahk
; 呪文を配列に格納
Spells := Object(1,"黄昏よりも昏きもの 血の流れより紅きもの 時の流れに埋もれし 偉大なる汝の名において 我ここに 闇に誓わん 我等が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて 等しく滅びを与えんことを!",2,"闇よりもなお昏きもの 夜よりもなお深きもの 混沌の海にたゆたいし 金色なりし闇の王 我ここに汝に願う 我ここに汝に誓う 我が前に立ち塞がりし すべての愚かなるものに 我と汝が力もて等しく滅びを与えんことを!")

for, index, Spell in Spells
{
    If (A_Index = 1)
    {
        SpellsV .= Spell "|" ; 1つ目の呪文を初期選択項目とする
        LongSpell := Spell
    }
    Else
    {
        SpellsV .= "|" Spell
        If (StrLen(Spell) > StrLen(LongSpell)) ; 現在のLongSpellより長ければLongSpellに代入
            LongSpell :=  Spell
    }
}

; 最長呪文の表示に必要な幅を取得
Gui, Add, Text, vSizer, %LongSpell%
GuiControlGet, LongSpellLen, Pos, Sizer
Gui, Destroy

LongSpellLenW += 25 ; プルダウンボタンの分拡大
gui, add, text, HwndSpellScroll, 詠唱する呪文を選択
gui, add, DropDownList, w%LongSpellLenW% vSpelltoCast, %SpellsV%
gui, add, Button, Default y+30, 詠唱する
gui, Show
WinWaitClose, ahk_id %SpellScroll%

Button詠唱する:
    gui, Submit
    gui, Destroy
    MsgBox, , %A_scriptname%, %SpelltoCast%
    Exitapp
GuiClose:
GuiEscape:
    gui, Destroy
    Exitapp

これで、スレイヤーズだろうがバスタードだろうがネギまだろうがUBWだろうがDies Iraeだろうが、ディスプレイの許す限り適切に表示できます。