インストールレスプログラミング( ´ー`)

VBA , JavaScript , HTAなど 365アプリはインストール必要ですが、仕事に無いケースはほぼないから(・_・;)

疑似掲示板 その2

TextStreamのCloseをきちんとすると,動きがとてもよくなったので,実際の使用に向けてさらに調整を加えてみました。

各授業で簡単に使えるように,データファイルに当たるbbs.txtを掲示板ファイルの名前を利用してつけるように変更。

同じフォルダにHTAファイルとtxtファイルがあるとごちゃごちゃなるので,bbsフォルダを作成してそこにtxtを保存するように変更。

見た目が悪かったので,CSSで若干調整。

(追記)更新ボタンも付け加えました。

この辺の手を加えてみました。試してみないと本当のとこわかりませんが,うまくいきそうな感じ。

ショートカットをうまく利用して生徒が簡単にアクセスできるようにもう一工夫があるといいのかな

さらにショートカットを固定できるように,仕組みを自分の中でルールづけられれば,面白いのかもしれない。

とりあえず,昨日のものよりは改善していると思うので,コードを載せます。

HTAのお約束で,メモ帳とかに貼って,文字コードを忘れずにUTF-8にして,拡張子htaで保存します。それだけでたぶん使えます。

HTAは手軽でいい。次のVersionのWindowsでも生き残らせてくれないかなぁ ( ´Д`)=3

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>メッセージ</title>
<!-- CSS -->
	<style type="text/css">
		.title{background-color: orange;font-size: 20px;display: block;width: 100%;}
		dt{display:block;width: 80px; float: left;padding-left: 2px;padding-top: 2px; background-color:lime;}
		dd{display:block;width: 410px;margin-left: 85px;padding-left: 5px;padding-top: 2px; background-color: greenyellow;}
		.user{display: none;}
		.form{background-color: beige;display: block;width: 100%-4px;padding: 4px;}
	</style>
<!--JScript-->	
<script>
	resizeTo(560,600);
	// FileSystemObject 用定数
	var ForReading = 1;
	var ForWriting = 8;
	/** BBS のデータファイルのフルパス */
	var thisName=location.pathname
	thisName=thisName.slice(thisName.lastIndexOf("\\")+1,thisName.lastIndexOf("."));
	var BbsFile = ".\\bbs\\"+thisName+"bbs.txt";
	var User=new ActiveXObject("WScript.Network").UserName;
	/** ファイルの読み込み */
	function loadText() {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var bbs = fso.OpenTextFile(BbsFile, ForReading);
		var tmp=bbs.ReadAll();
		bbs.Close();
		return(tmp);
	}
	/** 投稿内容のロード */
	function load() {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		if (fso.FolderExists(".\\bbs")==false) fso.CreateFolder(".\\bbs");
		if (fso.FileExists(BbsFile)==false){
			var bbs = fso.OpenTextFile(BbsFile, ForWriting,true);
			bbs.WriteLine("<dt style='background-color:limegreen;'>名前</dt><dd style='background-color:limegreen;'>メッセージ</dd><span class='user'>ユーザー名</span>\r");
			bbs.Close();
		} 
		document.getElementById("data").innerHTML=loadText();
	}
	/** 投稿 */
	function post() {
		var name=document.getElementById("name").value;
		var msg=document.getElementById("msg").value;
		var postData = "<dt>" + name + "</dt>" + "<dd>" + msg + "</dd>" +"<span class='user'>" + User +"</span>\r";
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var bbs = fso.OpenTextFile(BbsFile, ForWriting);
		bbs.WriteLine(postData);
		bbs.Close();
		load();
		document.getElementById("name").value="";
		document.getElementById("msg").value="";
	}
</script>
</head>

<body onLoad="load();">
	
	<div class="title">一言メッセージ</div>
	<span class="form">
		名前 <input type="text" id="name" size="14" value="" placeholder="Name"><br>
		メッセージ <input type="text" id="msg" size="50" value="" placeholder="Message"><br>
	  <input type="button" id="post" value="送信" onClick="post()">
		<input type="button" id="Reload" value="更新" onClick="load()">
	</span>
	<dl id="data"></dl>
</body>
</html>

動作画面はこんな感じです。

f:id:chemiphys:20190108134542p:plain

試用後に再調整かな。

今度はアンケートに近いものが作れるようにしてみよう。

疑似掲示板(失敗) JScript & HTA → 成功かも

興味がない分野について,自分の知識が全くないことに気づかせてくれるJSciptの学習。

今日は掲示板ぽいのを作ることをしていました。

neos21.hatenablog.com

こちらの記事を参考に,コードも使わせてもらいながら,自分の環境で作る。

JQueryを自分の環境でいまのところきちんと動かせなかったので,けっこうコードは行き当たりばったりで作ってます。

JScriptの学習と思ってこつこつやっていました。

例によってHTAです。HTMLでは動作しません。

メモ帳にはりつけて,拡張子HTAで保存します。文字コードUTF-8にしないと動かないようです。

追記 imihitoさんにコメントをいただいて,見直してみたらCloseに()が抜けていましたので修正しました。
  失敗を忘れないためにも下のほうの駄文は残しております。コードは書き直しました。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>メッセージ</title>
<!-- CSS -->
	<style type="text/css">
		dt{float: left;}
		dd{margin-left: 80px;}
		.user{display: none;}
	</style>
<!--JScript-->	
<script>
	// FileSystemObject 用定数
	var ForReading = 1;
	var ForWriting = 8;
	/** BBS のデータファイルのフルパス */
	var BbsFile = "bbs.txt";
	var User=new ActiveXObject("WScript.Network").UserName;
	/** ファイルの読み込み */
	function loadText() {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var bbs = fso.OpenTextFile(BbsFile, ForReading);
		var tmp=bbs.ReadAll();
		bbs.Close();
		return(tmp);
	}
	/** 投稿内容のロード */
	function load() {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		if (fso.FileExists(BbsFile)==false){
			var bbs = fso.OpenTextFile(BbsFile, ForWriting,true);
			bbs.WriteLine("<dt>名前</dt><dd>メッセージ</dd><span class='user'>ユーザー名</span>\r");
			bbs.Close();
		} 
		document.getElementById("data").innerHTML=loadText();
	}
	/** 投稿 */
	function post() {
		var name=document.getElementById("name").value;
		var msg=document.getElementById("msg").value;
		var postData = "<dt>" + name + "</dt>" + "<dd>" + msg + "</dd>" +"<span class='user'>" + User +"</span>\r";
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var bbs = fso.OpenTextFile(BbsFile, ForWriting);
		bbs.WriteLine(postData);
		bbs.Close();
		load();
		document.getElementById("name").value="";
		document.getElementById("msg").value="";
	}
</script>
</head>

<body onLoad="load();">
	<h1>一言メッセージ</h1>
	<p>
		名前 <input type="text" id="name" size="14" value="" placeholder="Name">
		  メッセージ <input type="text" id="msg" size="60" value="" placeholder="Message">
	</p>
	<p>
		<input type="button" id="post" value="送信" onClick="post()">
		※エラーが出たら少し待ってもう一度 送信 を押してください。
	</p>
	<dl id="data"></dl>
</body>
</html>

こんな画面になるとってもシンプルなもの。
送信ボタンを押せば投稿されるよ,ということと ユーザー名を裏で記録してるよ,というもの。
f:id:chemiphys:20190107225337p:plain


一人で試してても
f:id:chemiphys:20190107225633p:plain
こういうエラーがまぁまぁ出ます。書き込み時の競合なのかなーと思うけどテキストのファイルサイズもそんなに大きくないし,

なんでそれなりの確率でこいつが出てくるのか不思議です。できる範囲でCloseもやっているのになぁ。WriteLineの直後にClose書いたら拒否されるのでそこはやってません。

複数ウィンドウを開いて試すとさらにダメ。ということは実際の使用には現状はたえない状態のようです。元の記事の方とは書き込み回りも違いますし,そちらに

寄せていけば改善されるのだろうか。 ※追記 Close();をきちんとすることで改善されていると思われます。

とりあえず,この形で作りたかったので,その試しとして記録するとして,今度は各クライアントが一つのテキストを吐き出すようにして,あるフォルダにその小さいテキストを集める。

すでに存在する場合は上書きするという形にして,競合の可能性をなくす。

小さいテキストをかき集めて掲示板のデータにする,という形で今度は作ってみようと思います。

読み込みの際は問題にならないんじゃないかなと思うので,エラーを回避できるかなぁと思ったり。

明日はそれに挑戦してみよう。。

実際使っている授業支援ソフトが,アンケート機能が通信環境の安定性を損ねるということでなくなっています。

その代わりになるものを手にしたいと思っていますので,40名程度を相手にアンケートぽいものをサーバーレスで作ってみたいと思ってるところなんです。

そこまでたどり着くかどうかはさっぱりですが,楽しいですし,やってみたいところ。。

JScript mp3の再生について 書き残し

前回のタイマーで一番時間がかかった点の記録。

JavaScriptでmp3を鳴らすのはとても簡単だったのに,JScriptの場合はそうでなかった点。

というかそれに関する記述を探すのがとても大変でした。なので書き残し。

ちなみにわたしはDreamweaverを使ってJavascriptの学習をしています。。


Javascriptの場合はAudioオブジェクトがあるのでかなり楽でした。

        var pu = new Audio();
        pu.src = "pu.mp3";
        pu.volume = 0.5;
        pu.play();

これだけで音が鳴らせます。volumeについては0~1の間で設定。ほんとにすごく簡単。

でも,このままではHTAで,つまりJScriptではと言っていいんでしょうか,鳴らせません。

そこでやってるのが

      var pu= new ActiveXObject("MediaPlayer.MediaPlayer");
      pu.Volume=-10000; //音量を0に
      pu.FileName = "pu.mp3";
      pu.Volume=0;  //音量最大に  -10000~0 規定値 -600
      pu.play();

Createobjectに相当するやつなんでしょう。これで似たようにかける。

Audioタグを使ったり,ライブラリを使う方法は難しそうでめんどくさそうで 見送りました。

こちらの問題点はFileNameを設定したタイミングかな,そこで音がいきなりなります。

なので一度Volumeを最小に相当する-10000にして,適当なタイミングで音量をもどして使っています。

ひたすら検索しまくって探してたどりついたので,元記事がもうわかりません。。

見つけたら貼ります。

ここを除けばけっこうあっさりできてた気がしますので,ほんと苦労した内容でした。

年が明けてしばらく経ちますが。

のんびりしてたらもう5日も経ってました。

最近はVBAからは離れていますが,仕事が始まればまたデータ処理で思い出すんだと思います。

VBAからはすこし離れていますが,しばらく前に興味を持ったJavaScriptの勉強をしているところです。

全然わからない → 他人のコードを切り貼りしたり ちょこっとずつ付け足していくとなんとなく物が作れるようになってきました。

とても役に立ちそうな言葉だと思いますし,ブラウザを基本とすれば大概いろいろつくれそうなものなんだなぁというところまでは来ています。

でも,ローカルのファイルの操作となると File API とかいうやつを使わないといけないんだなぁ 面倒だなぁと思いながらそれでもいろいろといじってると,

HTA に出会いました。HTML Applications ってやつですね。

Windows10では少なくとも使えるみたいです。現在の職場ではOSはWindows10なので,少なくとも数年はいける。

JavaScript自身は HTML5CSS とセットでたぶんずーっと使えそうな予感ですので,そちら側はブラウザを元とするものについてはいけそうです。

HTAになると,微妙に JScriptとかVBScriptになり,ちょこっとずつ仕様が変わるので,思った通りにいかないことも多々ありますが,

 ローカルファイルにアクセスできる
 セキュリティに関して制限が甘い(警告類があんまりでない)
 htmlで気軽に書いてるのになんか立派なアプリケーションぽく見える
 もちろんインストールとかいらない

など,私にとってはかなりの魅力を持ったもののようです。

数年戦えますし,JavaScriptの学習にも十二分にもなりますので,OfficeのVBAJavaScriptHTAで遊び続けてものにしようと思います。

JQueryもローカルにライブラリ落としとけば私の職場の環境でもつかえそうです。JQueryまで理解すればたぶん本職ではないプログラマには十分かなぁと思ったり。。

本はひたすら買い漁りました。かなり出費してしまいましたが,長く使える知識になってくれると思っています。

さて,作ったものを載せてみます。

元はPowerPointVBAで作って授業中に使っていたタイマーと同じものなんですが,PowerPointってExcelのThisworkbookに相当するものがないので,他のスライドショーとの共存が難しいです。

基本はOneNote主体で授業を構成しているので,それでもいいんですが,PowerPointも使いたいよなぁと常々思っていました。

最初はブラウザで動くものをJavascriptで作り,満足していたんですが,表示の面でツールバーとかがいろいろ残るので不満が残るものが最初にできました。

CSSJavascriptがフルに使えるという意味でそちらを優先するものも今後いろいろ試してはみますが,タイマーはコンパクトであってほしい。そこは譲れない。

なので,いろいろな制限をのむ形でHTAで作ってみました。 メモ帳に張り付けて 文字コードUTF-8で保存します。拡張子はhtaで。。
音もなるように作ってるんですが,音を鳴らすためにはどこからかmp3を拾ってきて 同じフォルダに pu.mp3 start.mp3 end.mp3 とリネームして入れてください。

www.kurage-kosho.info
こちらのページなどの音がおすすめかも。クレジット表示なしでも使っていいですよ,と書かれていますので,コンパクトさ優先の私にはありがたいです。

アプリケーションを聞いてきたら Microsoft HTML アプリケーションホスト みたいなやつを選んでください。

<!DOCTYPE HTML>
<html lang="ja">
<HTA:APPLICATION SCROLL="no" NAVIGABLE="yes" CAPTION="yes" />

<head>
  <title></title>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <!--CSS-->
  <style type="text/css">
    a.Link{
      text-decoration: none;
      display: block;
      width: 100%;
      height: 100%;
    }
    a.Timer{
      text-decoration: none;
      font-size:160px;
      color:red;
      display: block; 
      line-height: 1.1;
      font-family:"Comic Sans MS", "cursive";
      text-align:center;
    }
    a:hover{background-color:yellow;}
    table {
      border-collapse: collapse
    }
    table td{
      text-align:center;
      width:150px;
      height:50px;
      border:1px solid black;
      font-size: 40px;
    }
  </style>
  <!--JavaScript記述-->
  <script type="text/javascript">
    var spanMyTime;//時間 を 表示 する SPAN
    var spanlog;
    var time0;
    var timer;//経過時間のTimer
    var timeval;
    var pu= new ActiveXObject("MediaPlayer.MediaPlayer");
      pu.Volume=-10000; //音量を0に
      pu.FileName = "pu.mp3";
    var endSound= new ActiveXObject("MediaPlayer.MediaPlayer");
      endSound.Volume=-10000; //音量を0に
      endSound.FileName="end.mp3";
    var startSound=new ActiveXObject("MediaPlayer.MediaPlayer");
      startSound.Volume=-10000;
      startSound.FileName="start.mp3";
    //初期処理(各種DOMを取得)
    function init() {
      timer=null;
      resizeTo(480, 290);
      document.body.style.overflow = "hidden";
      spanMyTime = document.getElementById("mytime");
      spanlog=document.getElementById("log");
      var time = ['10:00', '8:00', '7:00', '6:00', '5:00','4:00', '3:00', '2:00', '1:00', '0:30', '0:20','0:10'];
      var timesec = ['600', '480', '420', '360', '300', '240', '180', '120', '60', '30', '20','10'];
      var html = ['<table>'];
      for (var c = 0; c < 4; c++) {
        html.push('<tr>');
        for (var r = 0; r < 3; r++) {
          html.push('<td><a class="Link" href="#" onclick="startTimer(' + timesec[c+4*r] + ',true)">');
          html.push(time[c + 4 * r]);
          html.push('</a></td>');
        }
      }
      spanMyTime.innerHTML = html.join('');
      spanlog.innerHTML="";
    }

    function startTimer(tm,flg) {
      pu.Volume=0;  //音量最大に  -10000~0 規定値 -600
      pu.stop();
      endSound.Volume=0;  //音量最大に
      endSound.stop();
      startSound.Volume=0;
      startSound.play();
      timeval = tm;
      if (flg==true) time0=tm;
      spanlog.innerHTML="";
      // 既に 開始 し て いる 場合 は、 何 も し ない
      if (timer!= null) {
          stopTimer();
          return;
      }
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="stopTimer()">'+ timeConv(timeval) +'</a>';
      timer = setInterval(interval, 1000);
    }
    function stopTimer() {
      if (timer!= null) {
          clearInterval(timer);
      }
      // timer に null を 代入 する
      timer = null;
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="startTimer('+timeval+',false)">' + timeConv(timeval) + '</a>';
      spanlog.innerHTML='<a href="#" onClick="init()"> BACK </a>    <a href="#" onClick=startTimer('+time0+',false)> ReStart ' + timeConv(time0) + '</a>';
    }
    function interval() {
      // 経過 時間 を 表示
      timeval--;
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="stopTimer()">' + timeConv(timeval) + '</a>';
      if (timeval == 0) {
        clearInterval(timer);
        endSound.play();
        timer=null;
        spanlog.innerHTML='<a href="#" onClick="init()"> BACK </a>    <a href="#" onClick=startTimer('+time0+',false)> ReStart ' + timeConv(time0) + ' </a>';
        spanMyTime.innerHTML='<a class="Timer"  href="#" onclick="startTimer('+time0+')">' + timeConv(timeval) + '</a>';
      } else {
        if (timeval % 5 == 0) pu.play();
        if (timeval < 5) pu.play();
      }
    }
    function timeConv(tm) {
      var fun;
      var byou;
      fun = Math.floor(tm / 60);
      byou = ("0" + tm % 60).slice(-2);
      return fun + ":" + byou;
    }
  </script>
<!--JavaScript ここまで-->
</head>
<body onload="init()">
    <span id="mytime"></span>
    <span id="log"></span>
</body>
</html>

ちなみに,音を鳴らすたびに微妙に時間はずれていきますので 10分のを回すと音によるのかもしれませんが3秒くらいずれました。

気になる方は使えません。私は気にならないのでたぶん使います。

あとはBase64とかいうやつで文字列に直せば音もソースに組み込めるんでしょうか,そんな知識はないのでやってはいませんが,いろいろと奥は深いんでしょうね。。

今後もいろいろ試してみようと思っているところです。 

部品づくり 図形がどのセルの上にあるか

imihitoさんに教えていただいた application.caller TopLeftCell BottonRightCell これらはとても便利そう。

注意すべきは,自分で作り自分だけが使うときは問題ないけど少しだけ図形がずれる,ということがあると想定外の不具合はありそうです。


Altキー押しながらセルの上にきっちり図形を置くと,TopLeftCellは想定通り動きますが,BottomRightCellは右下のセルを返します。

自分の想定ではTopLeftCellのほうを使えばいい,となりますが,ほんのちょこっとずらされてしまうだけでだめになるはず。
f:id:chemiphys:20181119102419p:plain
なので,図形の真ん中の座標で判定させてみるテストをしてみました。

Sub test()
Dim Sht As Worksheet: Set Sht = ActiveSheet
Dim Shp As Shape: Set Shp = Sht.Shapes(Application.Caller)

Dim CenterX As Long, CenterY As Long
CenterX = Shp.Left + Shp.Width / 2
CenterY = Shp.Top + Shp.Height / 2

Dim Rng As Range: Set Rng = Sht.Range(Shp.TopLeftCell.Address & ":" & Shp.BottomRightCell.Address)
Dim r As Range
For Each r In Rng
    If CenterX >= r.Left And CenterX < r.Left + r.Width And CenterY >= r.Top And CenterY < r.Top + r.Height Then
        MsgBox r.Address
        Exit For
    End If
Next

f:id:chemiphys:20181119102432p:plain

負荷を減らすべく,評価対象のセルの数を少なくできてたらいいな。

もしこれらで取得するセルを利用する場合は,セルを間違ったら大変なことになるはずなので,基本的には図形名等にセルアドレスを忍ばせて,それを利用することで想定外は無くすべきだと考えますが,次善の策としてはかなり有用だなぁと思います。

それにしてもCallerプロパティはいいですね。これは今後コード書くときに重宝する気がしています。

メモ

Excelで図形を作ってOnActionを使ってみようとしていたら,次の記事を見つけました。

Excel VBA を学ぶなら moug モーグ | 即効テクニック | OnActionで実行するプロシージャに引数を渡す


図形にマクロをつけて,引数もつけれると(;´▽`A``

これはいい。

軽くテストしてみました。

Sub test()
    Dim Sht As Worksheet: Set Sht = ActiveSheet
    Dim Rng As Range: Set Rng = Sht.Range("c5:e8")

    Dim c As Range
    For Each c In Rng
        With Sht.Shapes.AddLabel(msoTextOrientationHorizontal, c.Left, c.Top, c.Width, c.Height)
            .Name = c.Address(False, False) & "_BTN"
            .OnAction = "'test2 """ & .Name & """ '"
            '.OnAction = "'test2 ""印刷""'"
        End With
    Next
    
End Sub


Sub test2(Str As String)
    MsgBox Str
End Sub

セルのシングルクリックイベントの代わりに図形をのせているので,各セルのleft,top,width,heightを使って図形を書き,それにOnActionをのっける。

セルのアドレスを捕まえることができるなら,いろいろとやれるのでそのためのものです。

testマクロを動かすと,
f:id:chemiphys:20181116220018p:plain
セルの上に図形をきれいにはりつけます。
無色透明にするなら .Fill.Visible=msoFalse をすればいいのかな。

これを実行すると,そのセルを選んだら(そこに貼り付けられている図形をクリックしたら)
f:id:chemiphys:20181116221039p:plain

このように,ちゃんとアドレスにしたがった値を返せます。

これはきっといいものな気がします。

onActionでマクロを貼り付けられた図形を動かすのはちょっと面倒なので,その点もいい感じ。

シートに貼る形のActiveXのリストボックスが,解像度が変わったり,タブレットで縦横表示が切り替わり続けると容易に表示が崩れたりと役立たずだったので,


リストボックス風なものを自分で実装したりしていました。
それを実現するためのいい手段が手に入った気がします。

追記 さらにメモ

d.hatena.ne.jp

のコメントのところから抜粋

参考にさせて頂き、助かりました。
第二引数の指定方法が分かりましたので、例示しておきます。
Sub AAAA(str1 As String, str2 As String)
.OnAction = "'AAAA ""引数1"", ""引数2""'"

きっといつか使う

さらにさらに追記

まぁこれも面白いわけですが,Powerpointの時もそうでしたが,セルに書く必要ないじゃないか という根本的なところにいきますね。。
セルにこだわらずに,加えた図形群でリストボックスぽいものをやる方法を考えていこう。

トグル入力のコード Excel VBA

ブログ名前詐欺中です。 今取り組んでることのための部品。

キーボードを使わずタブレットだけで作業をする。

複雑なインターフェースは準備できない。 でも簡単な入力方法を実装したい。

いつも思いつくのは,お風呂に入ってるときや,帰宅,出勤時。

考えを変えるタイミングって大事ですね。

さて,とても単純なヤツデス。

Sub クリック入力(Add As String, Min As Long, Max As Long)
    Dim Rng As Range: Set Rng = ActiveSheet.Range(Add)
    Rng.Value = Rng.Value + 1
    If Rng.Value > Max Then Rng.Value = Min
End Sub

Sub Sht1_E5()
    Call クリック入力("e5", 0, 5)
End Sub

Sub Sht1_E6()
    Call クリック入力("e6", 0, 1)
End Sub

(´▽`) '`,、'`,、 ミジカイ

f:id:chemiphys:20181114224804p:plain


私は雑な人間ですが,さすがに,上のSSはわざとちょっとずれてるのをスルーしています。

E5,E6のセル上にボタンにするための透明な図形をのせて,それらに Sht1_E5 Sht1_E6をマクロ登録しています。

動画とらないで大丈夫ですよね。。あまりにも短いコードですから。。

クリックするたびに数値が1ずつ増え,最大値を超えると最小値になるよ,というコードです。

Excelのリスト入力は便利なんですけど,キーボードやマウスが使えない時ってあれってかなりちっちゃい。

その時のためのインターフェースとして考えてみました。明日実装してみよう。。