Haskell でつくられたゲームが
に載ってゐた
それぞれ 色々と面白いのだが
のやうな ターミナルアプリに魅かれ
これに使はれてゐる brick といふライブラリを試してみたくなった
ドキュメント類も色々充實してゐる感じなのだが 素直にインストラクションに従はず
デモを なんとなく 試してみることにした
ちなみに stack はインストールしてある前提で話を進める
(はじめてHaskellをインストールする場合 今では GHCup がおすすめだと思ってゐる)
stack new bdemo
などとして bdemo といふ名のディレクトリをつくったら
中の app ディレクトリ内にある Main.hs の中身を デモプログラムに置き換へる
あと bdemo ディレクトリ内の package.yaml ファイル に いくつかパッケージを追加する
dependencies: - base >= 4.7 && < 5 - brick - microlens - microlens-th - microlens-mtl - vty
base >= 4.7 && < 5 といふのは 最初から書かれてゐるやつだ
その他は書いておかないと エラーになる
あとは
stack run
として ビルド 實行すればよい(すごい色々インストールされるから時間かかるけど)
今回使った デモコードは EditDemo.hs といふやつで
みたいな實行画面になる
さて 中身を見てみやうか
全體を一度載せる(まだ何だか分からん)
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RankNTypes #-} module Main where import Lens.Micro import Lens.Micro.TH import Lens.Micro.Mtl import qualified Graphics.Vty as V import qualified Brick.Main as M import qualified Brick.Types as T import Brick.Widgets.Core ( (<+>) , (<=>) , hLimit , vLimit , str ) import qualified Brick.Widgets.Center as C import qualified Brick.Widgets.Edit as E import qualified Brick.AttrMap as A import qualified Brick.Focus as F import Brick.Util (on) data Name = Edit1 | Edit2 deriving (Ord, Show, Eq) data St = St { _focusRing :: F.FocusRing Name , _edit1 :: E.Editor String Name , _edit2 :: E.Editor String Name } makeLenses ''St drawUI :: St -> [T.Widget Name] drawUI st = [ui] where e1 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1) e2 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit2) ui = C.center $ (str "Input 1 (unlimited): " <+> (hLimit 30 $ vLimit 5 e1)) <=> str " " <=> (str "Input 2 (limited to 2 lines): " <+> (hLimit 30 e2)) <=> str " " <=> str "Press Tab to switch between editors, Esc to quit." appEvent :: T.BrickEvent Name e -> T.EventM Name St () appEvent (T.VtyEvent (V.EvKey V.KEsc [])) = M.halt appEvent (T.VtyEvent (V.EvKey (V.KChar '\t') [])) = focusRing %= F.focusNext appEvent (T.VtyEvent (V.EvKey V.KBackTab [])) = focusRing %= F.focusPrev appEvent ev = do r <- use focusRing case F.focusGetCurrent r of Just Edit1 -> zoom edit1 $ E.handleEditorEvent ev Just Edit2 -> zoom edit2 $ E.handleEditorEvent ev Nothing -> return () initialState :: St initialState = St (F.focusRing [Edit1, Edit2]) (E.editor Edit1 Nothing "") (E.editor Edit2 (Just 2) "") theMap :: A.AttrMap theMap = A.attrMap V.defAttr [ (E.editAttr, V.white `on` V.blue) , (E.editFocusedAttr, V.black `on` V.yellow) ] appCursor :: St -> [T.CursorLocation Name] -> Maybe (T.CursorLocation Name) appCursor = F.focusRingCursor (^.focusRing) theApp :: M.App St e Name theApp = M.App { M.appDraw = drawUI , M.appChooseCursor = appCursor , M.appHandleEvent = appEvent , M.appStartEvent = return () , M.appAttrMap = const theMap } main :: IO () main = do st <- M.defaultMain theApp initialState putStrLn "In input 1 you entered:\n" putStrLn $ unlines $ E.getEditContents $ st^.edit1 putStrLn "In input 2 you entered:\n" putStrLn $ unlines $ E.getEditContents $ st^.edit2
はい わかりません
では まづ 最初の import文などは讀み飛ばして
data Name = Edit1 | Edit2 deriving (Ord, Show, Eq) data St = St { _focusRing :: F.FocusRing Name , _edit1 :: E.Editor String Name , _edit2 :: E.Editor String Name }
あたりから見ていくか
Name は 單に名前のことだと分かる
St は状態なんだらうけど focusRing って何だろ?
edit1 とか edit2 は デモ画面にある 二つのエディタのことを指すんだらう
次の makeLenses ''St みたいなところは
TemplateHaskell といふ 言語拡張を使って 何かつくってるんだ みたいな理解をしてゐる
だって St の左に チョンチョンって ふたつついてるでしょ?
これが TemplateHaskell の特徴なんだよ きっと
drawUI :: St -> [T.Widget Name] drawUI st = [ui] where e1 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1) e2 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit2) ui = C.center $ (str "Input 1 (unlimited): " <+> (hLimit 30 $ vLimit 5 e1)) <=> str " " <=> (str "Input 2 (limited to 2 lines): " <+> (hLimit 30 e2)) <=> str " " <=> str "Press Tab to switch between editors, Esc to quit."
drawUI ってからには 描画に関連してるんだらう
あっ! FocusRing でてきた
e1とe2 は エディタのことだらう
それに FocusRing がある ってことか?
ui のところは まさに Demo画面に描かれてゐる Input 1 とか Input 2 のことだな
色々位置などを設定してゐるのだらう
e1 とかで定義されてゐる
F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1)
をちょと 見ていかう
F は インポート文の
import qualified Brick.Focus as F
で指定されてゐるやうに Brick.Focus のことだ
その中の withFocusRing といふ函數を使用してゐる
これを Hackage で確認しやう
withFocusRing :: (Eq n, Named a n) => FocusRing n -> (Bool -> a -> b) -> a -> b
となってる
FocusRing n といふのが フォーカスリング(多分 エディットする部分が黄色くなるやうなやつだと思ふ)
(Bool -> a -> b) といふのが 真偽値と なんかを取って なんかを返す函數 (E.renderEditor (str .unlines) の部分)
a にあたるのが (st^.edit1)の部分
あぶないあぶない
ちょっと 深く見すぎた
次行ってみやう
appEvent :: T.BrickEvent Name e -> T.EventM Name St () appEvent (T.VtyEvent (V.EvKey V.KEsc [])) = M.halt appEvent (T.VtyEvent (V.EvKey (V.KChar '\t') [])) = focusRing %= F.focusNext appEvent (T.VtyEvent (V.EvKey V.KBackTab [])) = focusRing %= F.focusPrev appEvent ev = do r <- use focusRing case F.focusGetCurrent r of Just Edit1 -> zoom edit1 $ E.handleEditorEvent ev Just Edit2 -> zoom edit2 $ E.handleEditorEvent ev Nothing -> return ()
最初の行は 型注釈の部分
次からの appEvent 三つは 何か對應するキーを押すと 何かする ってやつだらう
focusNext とか 分かりやすい名前が付いてゐる
最後は 何か focusGetCurrent といふものの値によって 場合分けしてるな
今 ここに フォーカスしてゐるなら これをしろ みたいなことを 言っているんだらう・・・
initialState :: St initialState = St (F.focusRing [Edit1, Edit2]) (E.editor Edit1 Nothing "") (E.editor Edit2 (Just 2) "") theMap :: A.AttrMap theMap = A.attrMap V.defAttr [ (E.editAttr, V.white `on` V.blue) , (E.editFocusedAttr, V.black `on` V.yellow) ]
最初の状態を定義してゐるのは明らかだ
Edit1 Nothing
Edit 2 (Just 2)
とある
Nothing は 何行書いてもスクロールするエディタにするよ ってことだらう
Just 2 は 2行までに限定する といふことだ
あと theMap では 明らかに 白 青 黄色 といふ指定をしてゐるのがわかる
といふことは これが フォーカスされてゐるときと されてゐないときの色についてのデータだ!
appCursor :: St -> [T.CursorLocation Name] -> Maybe (T.CursorLocation Name) appCursor = F.focusRingCursor (^.focusRing)
なんだらう これは
カーソルについての指定さらうか
フォーカスリング カーソルといふのが あるんだらう (たぶん)
theApp :: M.App St e Name theApp = M.App { M.appDraw = drawUI , M.appChooseCursor = appCursor , M.appHandleEvent = appEvent , M.appStartEvent = return () , M.appAttrMap = const theMap }
アプリ全体の定義だらう
描画は drawUI で
イベント処理は appEvent で
スタートイベントは 特に指定がない といふことだらうか
あと 先程指定されてゐた フォーカス色の theMap が入ってゐる
main :: IO () main = do st <- M.defaultMain theApp initialState putStrLn "In input 1 you entered:\n" putStrLn $ unlines $ E.getEditContents $ st^.edit1 putStrLn "In input 2 you entered:\n" putStrLn $ unlines $ E.getEditContents $ st^.edit2
メイン函數
これが アプリを實行すると 最初に實行される
do 以下の一行目が Demo画面を表示させてゐる部分だ
Escキー を押すと エディタに入れてゐた情報を ターミナルに表示して終了するから
二行目以下は その部分だらう
うーん
まだちゃんと理解はできてゐないけど
Input2 の方にコマンドを打って リアルタイムに Input1 にログを表示させるのに向いてゐると思ふ
ちょうど 今 やってゐる kdbtl で さういふことをしたいと思ってゐたので これがそのまま使へるかも!!?
あとは タイマーイベントの部分を どう書くか 調べてみたい
とりあへず 今は こんな感じで