Source

sjl.bitbucket.org / clojure-lanterna / screens / index.html

Full commit
<!DOCTYPE html>
<html><head><meta charset="utf-8"/><title>Screens / clojure-lanterna</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><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">; =&gt; 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">; =&gt; :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">; =&gt; \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">; =&gt; [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>