BETA

【blessed.js】tabキーで要素間を移動する方法について

投稿日:2019-06-01
最終更新:2019-06-01
※この記事は外部サイト(https://qiita.com/uki00a/items/bb43f32d45d...)からのクロス投稿です

やりたいこと

blessed.jsでWebブラウザのようにtabキーで各要素間を移動したい。

formを使用する場合

formを作成する際に、keysオプションにtrueを指定するだけでよい。

const blessed = require('neo-blessed');  

const screen = blessed.screen({  
  autopadding: true,  
  smartCSR: true,  
  fullUnicode: true,  
});  

const form = blessed.form({  
  parent: screen,  
  keys: true // trueを指定しておくと、tabキーの押下を監視して、要素間を移動してくれる。  
});  

const name = blessed.textbox({  
  parent: form,  
  position: { width: 16, height: 3 },  
  inputOnFocus: true,  
  border: 'line',  
  name: 'name'  
});  

const submit = blessed.button({  
  parent: form,  
  position: { top: 3, height: 1, width: 8 },  
  name: 'submit',  
  content: '{center}Submit{/}',  
  tags: true,  
  style: {  
    bg: 'gray',  
    focus: {  
      bg: 'blue'  
    }  
  }  
});  

screen.key(['q'], () => process.exit(0));  
screen.render();  

formを使用しない場合

screen.focusNextメソッドを呼ぶことで各要素間を移動できるので、tabキーの押下時に呼びます。

ただし、textboxやtextareaのような入力要素があるとうまく動かないため、一工夫する必要があります。

const blessed = require('neo-blessed');  

const screen = blessed.screen({  
  autopadding: true,  
  smartCSR: true,  
  fullUnicode: true,  
  debug: true  
});  

// (1)  
const rooms = blessed.list({  
  parent: screen,  
  items: ['room-1', 'room-2', 'room-3'],  
  border: 'line',  
  keyable: true,  
  position: { height: 8, width: '20%' },  
  vi: true,  
  style: {  
    focus: { border: { fg: 'yellow' } },  
    selected: { bg: 'blue' }  
  }  
});  

const messages = blessed.log({  
  parent: screen,  
  border: 'line',  
  vi: true,  
  keyable: true,  
  position: { left: '20%', width: '80%', height: '80%' },  
  style: {  
    focus: { border: { fg: 'yellow' } },  
  }  
});  

const messageInput = blessed.textarea({  
  parent: screen,  
  position: { left: '20%', top: '80%', height: '15%' },  
  inputOnFocus: true,  
  keyable: true,  
  keys: true,  
  vi: true,  
  border: 'line',  
  name: 'name',  
  style: {  
    focus: { border: { fg: 'yellow' } }  
  }  
});  

const submit = blessed.button({  
  parent: screen,  
  position: { left: '20%', top: '95%', height: 1, width: 8 },  
  name: 'submit',  
  keyable: true,  
  content: '{center}Submit{/}',  
  tags: true,  
  style: {  
    bg: 'gray',  
    focus: {  
      bg: 'blue'  
    }  
  }  
});  

// (2)  
screen.focusNext();  

// (3)  
screen.on('element keypress', (el, ch, key) => {  
  if (key.full === 'tab') {  
    focusNext(screen);  
  }  
});  

function focusNext(screen) {  
  const isInputElement = typeof screen.focused.cancel === 'function';  
  if (isInputElement) {  
    const nextIndex = screen.children.indexOf(screen.focused) + 1;  
    const nextElement = screen.children[nextIndex];  
    screen.focused.setValue(removeLastChar(screen.focused.getValue()));  
    screen.focused.cancel();    
    screen.focusPush(nextElement);  
  } else {  
    screen.focusNext();  
  }  
}  

function removeLastChar(s) {  
  return s.substring(0, s.length - 1);  
}  

screen.key(['q'], () => process.exit(0));  
screen.render();  

解説

(1) フォーカスを当てたい各要素に対して、keyable: trueを指定する。
const rooms = blessed.list({  
  parent: screen,  
  items: ['room-1', 'room-2', 'room-3'],  
  border: 'line',  
  keyable: true,  
  position: { height: 8, width: '20%' },  
  vi: true,  
  style: {  
    focus: { border: { fg: 'yellow' } },  
    selected: { bg: 'blue' }  
  }  
});  

const messages = blessed.log({  
  parent: screen,  
  border: 'line',  
  vi: true,  
  keyable: true,  
  position: { left: '20%', width: '80%', height: '80%' },  
  style: {  
    focus: { border: { fg: 'yellow' } },  
  }  
});  
...  
(2) 先頭要素にフォーカスを当てる。
// (2)  
screen.focusNext();  
(3) tabキーの押下を監視して、次の要素へフォーカスを移す。

screeon.on('element keypress', callback)でスクリーン配下の要素のキー入力を監視できます。

入力されたキーは、callbackの第3引数から調べることができます。

screen.on('element keypress', (el, ch, key) => {  
  if (key.full === 'tab') {  
    focusNext(screen);  
  }  
});  

以下の関数で、次の要素へフォーカスを移す処理を行っています。

textareaやtextbox等の入力要素と非入力要素で処理を分岐させる必要があります。

入力要素の判定は、cancelメソッドの有無で行っています。

function focusNext(screen) {  
  const isInputElement = typeof screen.focused.cancel === 'function';  
  if (isInputElement) {  
    const nextIndex = screen.children.indexOf(screen.focused) + 1;  
    const nextElement = screen.children[nextIndex];  
    screen.focused.setValue(removeLastChar(screen.focused.getValue())); // tab文字が入力されてしまうため、削除しています。  
    screen.focused.cancel(); // 次の要素へ映るため、入力をキャンセルする。  
    screen.focusPush(nextElement); // screen.focusPushを呼ぶことで、指定した要素がフォーカスされます。  
  } else {  
    screen.focusNext(); // 非入力要素に対しては、単純にscreen.focusNextを呼ぶだけでよい。  
  }  
}  

function removeLastChar(s) {  
  return s.substring(0, s.length - 1);  
}  

参考

https://github.com/embark-framework/neo-blessed

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@uki00aの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!