clojure-lanterna/screens/index.html @ 515189492626
beast: Update site.
| author | Steve Losh <steve@stevelosh.com> |
|---|---|
| date | Tue, 14 Mar 2017 13:28:38 +0000 |
| parents | be9b7c8cdbbc |
| children | (none) |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Screens / clojure-lanterna</title> <link rel="stylesheet" href="../_dmedia/tango.css"/> <link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/> <script src="../_dmedia/less.js" type="text/javascript"> </script> </head> <body class="content"> <div class="wrap"> <header><h1><a href="..">clojure-lanterna</a></h1></header> <div class="markdown"> <h1 id="screens"><a href="">Screens</a></h1><p>The next layer of Lanterna is the screen layer. Think of screens as "<a href="https://en.wikipedia.org/wiki/Multiple_buffering#Double_buffering_in_computer_graphics">double buffering</a> for your console".</p> <p>Screens act as a buffer. You "draw" to the screen like you would normally draw directly to the terminal, but it doesn't appear to the user.</p> <p>When you're ready you tell the Screen to redraw. It will calculate the necessary changes and make them happen.</p> <p>This improves performance (TODO: elaborate) and makes it easy to avoid showing half-drawn UIs to your users.</p> <p>clojure-lantera's screen API is <em>very</em> similar to the terminal one. <strong>If you haven't read the <a href="../terminals/">Terminal documentation</a> you need to do that before you read this.</strong> This document moves very quickly because it assumes you've read the previous one.</p> <div class="toc"> <ul> <li><a href="#getting-a-screen">Getting a Screen</a></li> <li><a href="#writing-text">Writing Text</a><ul> <li><a href="#colors">Colors</a></li> <li><a href="#styles">Styles</a></li> </ul> </li> <li><a href="#moving-the-cursor">Moving the Cursor</a></li> <li><a href="#input">Input</a></li> <li><a href="#sizing">Sizing</a></li> <li><a href="#whats-next">What's Next?</a></li> </ul></div> <h2 id="getting-a-screen">Getting a Screen</h2> <p>Let's get started. Open up a REPL and pull in the namespace:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">lanterna.screen</span> <span class="ss">:as</span> <span class="nv">s</span><span class="p">])</span> </pre></div> <p>Much like getting a Terminal, you get a Screen with <code>get-screen</code>:</p> <div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">scr</span> <span class="p">(</span><span class="nf">s/get-screen</span> <span class="ss">:swing</span><span class="p">))</span> </pre></div> <p><code>get-screen</code> supports all the same types of console as <code>get-terminal</code>.</p> <p>You need to <code>start</code> and <code>stop</code> a Screen before/after use just like a Terminal:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/start</span> <span class="nv">scr</span><span class="p">)</span> <span class="c1">; ... do things ...</span> <span class="p">(</span><span class="nf">s/stop</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p>There's an <code>in-screen</code> helper too:</p> <div class="codehilite"><pre><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">scr</span> <span class="p">(</span><span class="nf">s/get-screen</span> <span class="ss">:swing</span><span class="p">)]</span> <span class="p">(</span><span class="nf">in-screen</span> <span class="nv">scr</span> <span class="c1">; do things with scr</span> <span class="p">))</span> </pre></div> <h2 id="writing-text">Writing Text</h2> <p>The screen layer uses a single function to draw to the screen: <code>put-string</code>. There is no single-character function like the terminal layer has.</p> <p><code>put-string</code> works like its Terminal counterpart:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">0</span> <span class="mi">0</span> <span class="s">"Hello, world!"</span><span class="p">)</span> </pre></div> <p>When you run this, nothing will happen. This is because Screens buffer their output. You need to redraw the screen to see any changes:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/redraw</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p><img alt="Screen" src="http://i.imgur.com/79Qr1.png"/></p> <p>You can of course queue up many updates before redrawing -- that's the whole point of a screen!</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">20</span> <span class="mi">10</span> <span class="s">"Hello, world!"</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">27</span> <span class="mi">10</span> <span class="s">"Steve"</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/redraw</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p><img alt="Screen with More" src="http://i.imgur.com/tLm16.png"/></p> <p>This will display "Hello, Steve!", which demonstrates that you can overwrite a single character as much as you want before a redraw and the correct result will be shown.</p> <p>Note that because we haven't touched the upper-left corner in this redraw our original "Hello, world!" is still there. Screens redraw <em>changes</em>, they don't start from scratch every time.</p> <p>If you want to remove old text you'll need to clear it out yourself by drawing over it with spaces.</p> <p>TODO: add a <code>clear-screen</code> function to make this suck less.</p> <h3 id="colors">Colors</h3> <p>Drawing colored text works a bit differently than the Terminal layer. Instead of specifying a color once and then everything you draw being that color, you specify the color in an option map alongside the string to draw:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">0</span> <span class="mi">12</span> <span class="s">"Red"</span> <span class="p">{</span><span class="ss">:fg</span> <span class="ss">:red</span><span class="p">})</span> <span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">0</span> <span class="mi">13</span> <span class="s">"Green"</span> <span class="p">{</span><span class="ss">:fg</span> <span class="ss">:green</span><span class="p">})</span> <span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">0</span> <span class="mi">14</span> <span class="s">"Yellow"</span> <span class="p">{</span><span class="ss">:fg</span> <span class="ss">:black</span> <span class="ss">:bg</span> <span class="ss">:yellow</span><span class="p">})</span> <span class="p">(</span><span class="nf">s/redraw</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p><img alt="Screen with Colors" src="http://i.imgur.com/uC1qk.png"/></p> <h3 id="styles">Styles</h3> <p>Currently broken, sorry.</p> <h2 id="moving-the-cursor">Moving the Cursor</h2> <p>Just like the terminal layer, you might want to move the cursor when using a Screen.</p> <p>There's a <code>move-cursor</code> function that works like the terminal one to do this:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">5</span> <span class="mi">5</span> <span class="s">"@"</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/move-cursor</span> <span class="nv">scr</span> <span class="mi">5</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/redraw</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p><img alt="Screen with Cursor Moved" src="http://i.imgur.com/gQ2FO.png"/></p> <p>Notice that you have to redraw the screen before the cursor will actually move.</p> <p>The cursor will stay where you put it, even after other updating and redraws:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">5</span> <span class="mi">5</span> <span class="s">" "</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/put-string</span> <span class="nv">scr</span> <span class="mi">6</span> <span class="mi">5</span> <span class="s">"@"</span><span class="p">)</span> <span class="p">(</span><span class="nf">s/redraw</span> <span class="nv">scr</span><span class="p">)</span> </pre></div> <p><img alt="Screen with Cursor Unmoved" src="http://i.imgur.com/XTd1I.png"/></p> <p>See how the cursor is still in the original spot (5, 5)? If you want it to move you need to tell it to move with <code>move-cursor</code>.</p> <h2 id="input">Input</h2> <p>Getting input works exactly like the terminal layer:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/get-key</span> <span class="nv">scr</span><span class="p">)</span> <span class="c1">; => nil</span> <span class="p">(</span><span class="nf">s/get-key-blocking</span> <span class="nv">scr</span><span class="p">)</span> <span class="c1">; => :page-down</span> <span class="p">(</span><span class="nf">s/get-key</span> <span class="nv">scr</span><span class="p">)</span> <span class="c1">; => \S</span> </pre></div> <p>Go back and read the terminal docs if you don't understand those functions.</p> <h2 id="sizing">Sizing</h2> <p>Sizing works the same way as the terminal layer. Screen have a <code>get-size</code> function of their own:</p> <div class="codehilite"><pre><span class="p">(</span><span class="nf">s/get-size</span> <span class="nv">scr</span><span class="p">)</span> <span class="c1">; => [130 44]</span> </pre></div> <p>You can pass a resize listening function when you create your screen:</p> <div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">screen-size</span> <span class="p">(</span><span class="nb">ref </span><span class="p">[</span><span class="mi">0</span> <span class="mi">0</span><span class="p">]))</span> <span class="p">(</span><span class="kd">defn </span><span class="nv">handle-resize</span> <span class="p">[</span><span class="nv">cols</span> <span class="nv">rows</span><span class="p">]</span> <span class="p">(</span><span class="nb">dosync </span><span class="p">(</span><span class="nb">ref-set </span><span class="nv">screen-size</span> <span class="p">[</span><span class="nv">cols</span> <span class="nv">rows</span><span class="p">])))</span> <span class="p">(</span><span class="k">def </span><span class="nv">scr</span> <span class="p">(</span><span class="nf">s/get-screen</span> <span class="ss">:swing</span> <span class="p">{</span><span class="ss">:resize-listener</span> <span class="nv">handle-resize</span><span class="p">}))</span> </pre></div> <p>Go back and read the terminal docs for the full story.</p> <h2 id="whats-next">What's Next?</h2> <p>Now that you can use the screen layer for double-buffered console rendering you've got pretty much everything you need. Go make something!</p> <p>The <a href="../reference/">Reference documentation</a> has all the detailed information you'll probably find yourself looking for once you actually dive in and start building.</p> </div> <footer><p>Created by <a href="http://stevelosh.com">Steve Losh</a>. Documentation created with <a href="http://sjl.bitbucket.org/d/">d</a>.</p> <p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p> <script type="text/javascript"> var _gauges = _gauges || []; (function() { var t = document.createElement('script'); t.type = 'text/javascript'; t.async = true; t.id = 'gauges-tracker'; t.setAttribute('data-site-id', '4f843f8c613f5d65280000e6'); t.src = '//secure.gaug.es/track.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(t, s); })(); </script></footer> </div> </body> </html>