今回試すのは CustomEventDemo.hs といふ brick のデモだ
前回と同様に stack を使って 走らせてみた
コードの全貌は以下
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module Main where import Lens.Micro ((^.)) import Lens.Micro.TH (makeLenses) import Lens.Micro.Mtl import Control.Monad (void, forever) import Control.Concurrent (threadDelay, forkIO) import qualified Graphics.Vty as V import Brick.BChan import Brick.Main ( App(..) , showFirstCursor , customMain , halt ) import Brick.AttrMap ( attrMap ) import Brick.Types ( Widget , EventM , BrickEvent(..) ) import Brick.Widgets.Core ( (<=>) , str ) data CustomEvent = Counter deriving Show data St = St { _stLastBrickEvent :: Maybe (BrickEvent () CustomEvent) , _stCounter :: Int } makeLenses ''St drawUI :: St -> [Widget ()] drawUI st = [a] where a = (str $ "Last event: " <> (show $ st^.stLastBrickEvent)) <=> (str $ "Counter value is: " <> (show $ st^.stCounter)) appEvent :: BrickEvent () CustomEvent -> EventM () St () appEvent e = case e of VtyEvent (V.EvKey V.KEsc []) -> halt VtyEvent _ -> stLastBrickEvent .= (Just e) AppEvent Counter -> do stCounter %= (+1) stLastBrickEvent .= (Just e) _ -> return () initialState :: St initialState = St { _stLastBrickEvent = Nothing , _stCounter = 0 } theApp :: App St CustomEvent () theApp = App { appDraw = drawUI , appChooseCursor = showFirstCursor , appHandleEvent = appEvent , appStartEvent = return () , appAttrMap = const $ attrMap V.defAttr [] } main :: IO () main = do chan <- newBChan 10 void $ forkIO $ forever $ do writeBChan chan Counter threadDelay 1000000 let buildVty = V.mkVty V.defaultConfig initialVty <- buildVty void $ customMain initialVty buildVty (Just chan) theApp initialState
元の リポジトリにあるコードを ちょっと改變した (バージョンが十分新しいので cppを使ふ部分は なくて良いと判断した)
とてもシンプルな デモ画面だが
Counter value is:
の數字が 一秒ごとに 1ずつ 増へていく
data CustomEvent = Counter deriving Show
これは それらしく言へば CustomEvent型の データコンストラクタ が Counter といふ名前で定義されてゐる といふことでいいのかな
data St = St { _stLastBrickEvent :: Maybe (BrickEvent () CustomEvent) , _stCounter :: Int } makeLenses ''St
状態を表すStは 要素が二つあって ひとつは明らかに カウントを数えるものだが 最初の要素は 「最後のbrickイベント」といふ名前になってゐる
これが イベントを受けとったりするやつなのだらうか
makeLenses のところの理解は 後回しにしやう〜(前回も出てきたけど)
まあ ちょっと 軽く考へておくと Lens(讀みは レンズ でいいのかな) といふのは
「Haskell入門」の267ページによると
「複雑なデータ構造への効率的なアクセス」
をするためのものーーださうだ
よし! あとで 勉強しよー
といふわけで 今は無視
drawUI :: St -> [Widget ()] drawUI st = [a] where a = (str $ "Last event: " <> (show $ st^.stLastBrickEvent)) <=> (str $ "Counter value is: " <> (show $ st^.stCounter))
str といふのは 前回も出てきたが スルーしてゐた
まあ 文字表示に關するものだらうことは予想できるのだが
ちょっと 見ておくか
import文から分かるやうに str は
import Brick.Widgets.Core モジュールの函數のはずだ
Hackage で確認する
str :: String -> Widget n
となってゐる
やはり 文字列を取って ウイジットなるものを返す函數だ
確か brickの説明にちょろっと 書いてあったと思ふのだが
この brickといふやつは ウイジットといふものの集合體で アプリを實行してゐる といふことらしいから
brickを使ふからには この ウイジットヘの理解が必須なのだらう (まだ全然理解してないけど)
デモ画面を見ると
Last event: Just (AppEvent Counter)
となってゐる
drawUI によれば この Just (AppEvent Counter)
といふのは st^.stLastBrickEvent つまり 状態Stの中の stLastBrickEvent の値を表してゐる
ふーん AppEvent といふ ブリックのイベント があって それが Counter っていふ名前なのね〜 ていふ理解かな 今は
appEvent :: BrickEvent () CustomEvent -> EventM () St () appEvent e = case e of VtyEvent (V.EvKey V.KEsc []) -> halt VtyEvent _ -> stLastBrickEvent .= (Just e) AppEvent Counter -> do stCounter %= (+1) stLastBrickEvent .= (Just e) _ -> return ()
前回もあった appEvent函數だ
case文の中にある最初の行は Escキーを押すイベントだ
haltは中断ってことだろう (前回もさうだったと思ふ)
ちなみに %= とか .= とかの謎オペレータは
import Lens.Micro.Mtl
でインポートされたやうだ
まあ 明らかに %= (+1) は stCounterに1を加えて代入
.=は 單純に置き換へ といふのが見てとれる
しかし あれだな〜 ここで 状態變數(?)を操作できるんだな〜
initialState :: St initialState = St { _stLastBrickEvent = Nothing , _stCounter = 0 } theApp :: App St CustomEvent () theApp = App { appDraw = drawUI , appChooseCursor = showFirstCursor , appHandleEvent = appEvent , appStartEvent = return () , appAttrMap = const $ attrMap V.defAttr [] }
initialStateについては 明解だらう
theApp の カーソルの定義らしきものが showFirstCursor になってゐる
前回は 何だっけ appCursor といふのになってゐて
それが フォーカスリングカーソル みたいなものによって定義されてたな〜
たぶん今回は 「普通の」カーソルっていふことか?
デモ画面には カーソルらしきものは表示されてゐないが・・・
AttrMapの部分は attrMapといふ函數を使ってゐる (Brick.AttrMap をインポートしてゐる)
今は ここは深入りしない
main :: IO () main = do chan <- newBChan 10 void $ forkIO $ forever $ do writeBChan chan Counter threadDelay 1000000 let buildVty = V.mkVty V.defaultConfig initialVty <- buildVty void $ customMain initialVty buildVty (Just chan) theApp initialState
さすがに 最初の newBChan 10 は氣になるので 少し調べた
モジュール Brick.BChan で
BChan といふデータは
BChan is an abstract type representing a bounded FIFO channel.
と書かれてゐる
有限の FIFOチャネルを表す?
FIFO って First In First Out ってこと? 情報技術者試験の勉強で出てきたやうな・・・
スタックみたいなもんだと 考へていいのかな・・・
んで newBChan 10 といふのは そのチャネルを10個まで持てるやうなデータ BChanをつくるってこと?
void や forever は Control.Monad モジュールから來てゐる
void は 結果を返さないよってことだらう
forever は その名の通り ずっと繰り返せってこと だと思ふ
threadDelay の百万が 1秒にあたるんだ〜
てことは threadDelay の 1 は 百万分の1秒! すごいな〜
前回のやつと 今回のやつを うまく組み合はせて 例へば stLastBrickEvent の値を Input2に stCounter の値を Input1に表示したりできるかな〜
今度それに挑戦してみやうかな