Sui ― GUI Library for LÖVE (1) フォーカス
上の2つに次いで、3つ目のSuiの記事。
今日はフォーカス機能を実装した。これがあれば、フォーカスのあるウィジェットだけ反応するようにもできる。
前回書いた通り、Suiのウィジェットは単なる関数の詰め合わせなので、新しい機能を追加したい時は、新しい関数を追加してやるだけで済む。フォーカス機能を実現するために、changefocusとfocusという2つの関数が追加され、これらの関数を操作するビルダー関数も用意されている。
大事なビルダー関数は、sui.focusstop, sui.focusroot, sui.focusの3つだ。
ui = sui.focusroot sui.vbox 5, { sui.focusstop sui.focusbc {64, 64, 64, 255}, sui.label 200, 24, "Hello, world!" sui.focusstop sui.focusbc {64, 64, 64, 255}, sui.pie 100, 0.8 sui.focusstop sui.bc {32, 32, 32, 255}, sui.focusbc {64, 64, 64, 255}, sui.hbar 200, 16, 0.8 }
sui.focusstopはウィジェットにchangefocus関数を追加するビルダー関数。changefocus関数を持っているウィジェットは、フォーカスを持てるようになる。フォーカスが移動したときにはchangefocus関数が連鎖的に呼ばれ、フォーカスを得た時と失った時に、子ウィジェットのfocusイベントハンドラーが呼び出されるという構成になっている。
sui.focusrootはchangefocus関数を「閉じる」ためのビルダー関数。changefocusはフォーカスをリレーして伝えるが、フォーカスが一巡したらまた最初のウィジェットにフォーカスを戻す処理を追加する。
sui.focusbcはフォーカスを持っているときに背景色をつけるビルダー関数で、sui.focusで実装されている。sui.focusはfocusイベントハンドラーを追加する。
sui.focusbc = (color, widget) -> focused = false sui.focus (f) -> focused = f, sui.bc (-> if focused then bang(color)), widget
sui.vboxなどが生成するコンテナーウィジェットは、子ウィジェットのchangefocusを順番に呼び出していくので、コンテナーが入れ子になっている場合や、focusstopが入れ子になっている場合も適切にフォーカスがリレーするようになっている。
フォーカスの移動は、ウィジェットのchangefocus関数を呼び出す。実引数として渡しているのはフォーカス移動の方向で、trueを渡すと逆順に移動する。この例では、シフトキーを押している場合に逆向きの移動になる。
love.keypressed = (key, unicode) -> switch key when 'tab' ui.changefocus(love.keyboard.isDown('lshift', 'rshift')) return
この例では、フォーカスはキーボードで操作した場合にだけ移動する。マウスクリックでフォーカスを移動させる場合には、フォーカスを指定したウィジェットに移動させる処理がいるが、そのためにはchangefocusに移動させる先のウィジェットを渡せば良い。少しわずらわしいが、次のclick_focusstopのようなビルダー関数を用意することで、クリックでフォーカスを移す処理を実現できる。
local ui click_focusstop = (widget) -> local obj obj = sui.focusstop sui.mousepressed (-> ui.changefocus(obj)), widget return obj ui = sui.focusroot sui.vbox 5, { click_focusstop sui.focusbc {64, 64, 64, 255}, sui.label 200, 24, "Hello, world!" click_focusstop sui.focusbc {64, 64, 64, 255}, sui.pie 100, 0.8 click_focusstop sui.bc {32, 32, 32, 255}, sui.focusbc {64, 64, 64, 255}, sui.hbar 200, 16, 0.8 }
フォーカスを持っているときだけ反応するイベントハンドラーを追加するsui.focuseventを使えば、テキストボックスやキーボードで操作できるボタンを実装できる。
toggleButton = -> press = -> state = not state sui.focusevent 'keypressed', ((key, unicode) -> if key == 'return' or key == ' ' then press()), sui.clicked press, sui.bc {50, 50, 50}, sui.margin 10, 10, sui.focusfc {255, 255, 128, 255}, sui.label 60, 16, (-> if state then "[ ] Stop" else "|> Play") textbox = -> text = '' sui.vbox 5, { sui.focusoption { [true]: sui.label 200, 16, -> "Type away! #text = " .. tostring(#text) [false]: sui.label 200, 16, -> "Focus on me!" } sui.focusevent 'keypressed', (key, unicode) -> if key == 'backspace' text = string.sub(text, 1, -2) elseif 0x20 <= unicode and unicode < 0x7F text ..= string.char(unicode), sui.label 200, 16, -> text }
デモ動画