tcellについて2

Posted on:

これまで

  1. goのTUIについて
  2. tcellについて

イベント

tcellのイベントは、NewScreen()で作成したスクリーンのPollEvent()で取得できます。

その名の通り、イベントが起こるまでポーリング(polling)して待つので、起こらない限り止まったままになります。

PollEvent()でイベントが起こったらイベントに応じて処理し、SetContent()でセットし、次のイベントが起こる前にDraw()で描画する。 というのが、実際のメインルーチンになります。

このメインルーチンをgoroutineで動かし、終了のイベントがきたらchannelに通知して通知を受信したらFini()を実行して終了するのが一般的な流れです。

キーイベント

イベントの中でも重要でよく使用するのがキーイベントです。 以下のプログラムは左上に打ったキーが表示されます。ESCキー又はEnterキーで終了します。

package main

import (
	"log"
	"time"

	"github.com/gdamore/tcell"
)

func main() {
	screen, err := tcell.NewScreen()
	if err != nil {
		log.Fatal(err)
	}
	if err = screen.Init(); err != nil {
		log.Fatal(err)
	}
	defer screen.Fini()

	screen.SetContent(0, 0, '_', nil, tcell.StyleDefault)

	quit := make(chan struct{})
	go func() {
		for {
			screen.Show()
			ev := screen.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyEscape, tcell.KeyEnter:
					close(quit)
					return
				case tcell.KeyRune:
					screen.SetContent(0, 0, ev.Rune(), nil, tcell.StyleDefault)
					time.Sleep(time.Millisecond * 100)
				}
			}
		}
	}()
	<-quit
}

キーイベントデモ キーイベントデモ

ev := screen.PollEvent() でイベントを待機して、イベントが起こったらevに入ります。 tcellのイベントはinterfaceで受け取れるので、さまざまなイベントを入れることができます。

さまざまなイベントが入ってくるのでev.(type)によりイベントのタイプを判別します。 キーイベントは*tcell.EventKeyです。 この判別したときにswitch ev := ev.(type)で、イベントタイプをキャストしてevに入れ直すことで、 case内のイベントでは、evに実際のイベントタイプが渡ります。

その結果*tcell.EventKeyでは、ev.Key()でキーイベントが取得できるようになります。

ev.Key()ではtcellで定義されているキーが取得できます。通常表示されないような特殊なキー(Escapeキーはtcell.KeyEscapeのように)はここで取得できます。 tcell.KeyEscapetcell.KeyEnterを取得して終了処理をしています。

表示されるようなキーはtcell.KeyRuneで判別できて、ev.Rune()によりruneとして取得できます。 今回は取得したruneをそのままSetContentでセットしています。

runeとして取得できるのでインプットメソッドで入力した日本語等も確定した後に一文字づつ取得できます。 分かりやすいように表示した後0.1秒Sleepしています。

イベントのループの前でscreen.Show()をして、実際の描画しています。

また、日本語入力だけでなく、コピーペーストで貼り付けてもキー入力として取得できます。 逆に言うと端末側が処理するので、基本的に入力方法は判別できません。

マウスイベント

マウスイベントは端末側が対応している必要がありますが、*tcell.EventMouseで、イベントが取得できます。

tcellの_demos/mouse.go がよくできているので、そちらを参照するのが良いでしょう。

リサイズイベント

端末のサイズはリサイズされる場合があります。そうすると端末の全画面を使用しているtcellでは、再描画する必要があります。 その際には、*tcell.EventResizeイベントで、イベントを取得し画面サイズをscreen.Size()で取得して再描画をおこないます。