<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Daniel Duan's Articles About TUI</title>
        <link>https://duan.ca/tag/tui/</link>
        <atom:link href="https://duan.ca/tag/tui/feed.xml" rel="self" type="application/rss+xml" />
            <item>
                <title>Making TUI Applications In Swift</title>
                <description>&#60;p&#62;One of my fondest memory in programming is writing the game Snake on one of
these:&#60;/p&#62;
&#60;p&#62;&#60;img src=&#34;/assets/2016/12/e-dictionary.jpg&#34; alt=&#34;An Electronic/Digital Dictionary&#34; /&#62;&#60;/p&#62;
&#60;p&#62;At the time, I was in a very restrictive school where a real computer wasn&#39;t
permited in class. The actual thing (that&#39;s an &#38;quot;electronic dictionary&#38;quot; by the
way) was probably older, slower and of a different brand than the one in the
photo. In fact, I didn&#39;t know how to make a copy of the code, and the device
wasn&#39;t even mine. So the game just stayed there when the device was returned to
the owener and its fate unknown.&#60;/p&#62;
&#60;p&#62;But I love it nontheless. Design the game, write it, playing it, showing it off&#60;/p&#62;
&#60;ul&#62;
&#60;li&#62;everything.&#60;/li&#62;
&#60;/ul&#62;
&#60;br /&#62;
&#60;p&#62;Fastforward to today. I use and write software applications for a living.
Programs I use directly range from terminal commands to GUI apps and web apps in
a browser.&#60;/p&#62;
&#60;p&#62;Some programs, however, don&#39;t belong to those categories: vim, tig, irssi, etc.
They run in a terminal like CLI commands. But unlike commands like &#60;code&#62;ls&#60;/code&#62;, they
take over the entire buffer to offer different modes of interaction. You can
use hotkeys to navigate menus, trigger functions and so on.  Sometimes you can
use mouse, too. Their layout often resemble that of a GUI application running in
it&#39;s own &#38;quot;window&#38;quot;:&#60;/p&#62;
&#60;p&#62;&#60;img src=&#34;/assets/2016/12/tig.png&#34; alt=&#34;tig&#34; /&#62;&#60;/p&#62;
&#60;p&#62;Just to make things painfully clear, these apps&#39;s interface is composed
exclusively of text. We can vaguely categorize them as &#38;quot;Text-based User
Interface(TUI)&#38;quot; application.&#60;/p&#62;
&#60;p&#62;There are many reasons to like TUI applications. Compared to a CLI program,
they can display information in 2-dimentional, more organized layout (aka more
like a GUI app). Unlike a GUI application, they almost only require a terminal
to run. If you ssh onto a Linux server on your iPad, they&#39;d work the same as
running locally on a Mac. For people who live in terminal simulators, it&#39;s nice
to run apps that don&#39;t open a new Window. Oh, they often tend to look the same
no matter how your OS changes.&#60;/p&#62;
&#60;p&#62;Most importantly, they remind me of the programs I built on that electronic
dictionary. I want to make them today, with my favorite programming language
Swift!&#60;/p&#62;
&#60;br /&#62;
&#60;p&#62;The go-to library for writing TUI apps is &#60;a href=&#34;https://www.gnu.org/software/ncurses/&#34;&#62;ncurses&#60;/a&#62;. Classics such as vim,
emacs and mutt were written with it to some degree. But I find it hard to dive
into: it has a large number of strange legacy function names and offers
pre-built UI elements like panels, menus, forms etc. The terminal in my mind is
a spread of characters that can be updated by me. That sounds fun. Ncurses seems
more like UIKit :P.&#60;/p&#62;
&#60;p&#62;Luckily, I found the fun library: &#60;a href=&#34;https://github.com/nsf/termbox&#34;&#62;termbox&#60;/a&#62; by &#60;a href=&#34;https://github.com/nsf&#34;&#62;nsf&#60;/a&#62;. Termbox offers around
a dozen functions that gives us size of the terminal, a way to update text at
a row/column coordinates, and user inputs in forms of key-presses and
mouse-clicks. And that&#39;s it!&#60;/p&#62;
&#60;p&#62;After making &#60;a href=&#34;https://github.com/dduan/Termbox&#34;&#62;a Swift wrapper&#60;/a&#62;, I was able to build a painter app in
&#60;a href=&#34;https://github.com/dduan/TerminalPaint&#34;&#62;a handleful of lines&#60;/a&#62;:&#60;/p&#62;
&#60;p&#62;&#60;img src=&#34;/assets/2016/12/terminal-paint.png&#34; alt=&#34;terminal paint&#34; /&#62;&#60;/p&#62;
&#60;p&#62;So that&#39;s an TUI app drawing according to mouse clicks built with only a few
primitives from termbox. Awww yeah! Here&#39;s the code with the comment stripped,
to give you a taste:&#60;/p&#62;
&#60;pre&#62;&#60;code class=&#34;language-swift&#34;&#62;import Termbox
func printAt(x: Int32, y: Int32, text: String,
    foreground: Attributes = .default,
    background: Attributes = .default)
{
    let border = Termbox.width

    for (c, xi) in zip(text.unicodeScalars, x ..&#38;lt; border) {
        Termbox.put(x: xi, y: y, character: c,
            foreground: foreground, background: background)
    }
}

func updateHelp(drawing: UnicodeScalar) {
    let lastY = Termbox.height - 1
    let content = [
        &#38;quot;Drawing [\(drawing)]&#38;quot;,
        &#38;quot;Press other character to change&#38;quot;,
        &#38;quot;Use mouse to draw&#38;quot;,
        &#38;quot;Press &#39;q&#39; to quit&#38;quot;
    ].joined(separator: &#38;quot; | &#38;quot;)

    let filler = String(repeating: &#38;quot; &#38;quot;,
        count: Int(Termbox.width) - content.unicodeScalars.count)

    printAt(x: 0, y: lastY, text: content + filler,
        foreground: .white, background: .blue)
}

func paint() {
    do {
        try Termbox.initialize()
    } catch let error {
        print(error)
        return
    }
    Termbox.inputModes = [.esc, .mouse]

    var drawingCharacter: UnicodeScalar = &#38;quot;.&#38;quot; {
        didSet {
            updateHelp(drawing: drawingCharacter)
        }
    }

    updateHelp(drawing: drawingCharacter)

    Termbox.present()

    outer: while true {
        guard let event = Termbox.pollEvent() else {
            continue
        }

        switch event {
        case let .character(_, value):
            if value == &#38;quot;q&#38;quot; {
                break outer
            }

            drawingCharacter = value

        case let .key(_, value):
            if value == .space {
                drawingCharacter = &#38;quot; &#38;quot;
            }

        case let .mouse(x, y):
            Termbox.put(x: x, y: y,
                character: drawingCharacter,
                foreground: .red)
        default:
            continue
        }

        Termbox.present()
    }

    Termbox.shutdown()
}

paint()
&#60;/code&#62;&#60;/pre&#62;
&#60;p&#62;&#60;a href=&#34;https://github.com/dduan/Termbox&#34;&#62;Library&#60;/a&#62; and this &#60;a href=&#34;https://github.com/dduan/TerminalPaint&#34;&#62;app&#60;/a&#62; are both on Github. Go and
have fun :)&#60;/p&#62;
</description>
                <pubDate>Mon, 26 Dec 2016 21:39:05 -0800</pubDate>
                <link>https://duan.ca/2016/12/26/making-tui-programs-in-swift/</link>
                <guid isPermaLink="true">https://duan.ca/2016/12/26/making-tui-programs-in-swift/</guid>
            </item>
    </channel>
</rss>