pax_global_header00006660000000000000000000000064132634301770014520gustar00rootroot0000000000000052 comment=c055c87ae801372cd74a0839b972db4f7697ae5f gocui-0.4.0/000077500000000000000000000000001326343017700126275ustar00rootroot00000000000000gocui-0.4.0/.gitignore000066400000000000000000000000061326343017700146130ustar00rootroot00000000000000*.swp gocui-0.4.0/AUTHORS000066400000000000000000000013321326343017700136760ustar00rootroot00000000000000# This is the official list of gocui authors for copyright purposes. # Names should be added to this file as # Name or Organization contribution # Contribution # The email address is not required for organizations. Roi Martin Main developer Ryan Sullivan Toggleable view frames Matthieu Rakotojaona Wrapped views Harry Lawrence Basic mouse support Danny Tylman Masked views Frederik Deweerdt Colored fonts Henri Koski Custom current view color Dustin Willis Webber 256-colors output mode support gocui-0.4.0/LICENSE000066400000000000000000000027601326343017700136410ustar00rootroot00000000000000Copyright (c) 2014 The gocui Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the gocui Authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gocui-0.4.0/README.md000066400000000000000000000064761326343017700141230ustar00rootroot00000000000000# GOCUI - Go Console User Interface [![GoDoc](https://godoc.org/github.com/jroimartin/gocui?status.svg)](https://godoc.org/github.com/jroimartin/gocui) Minimalist Go package aimed at creating Console User Interfaces. ## Features * Minimalist API. * Views (the "windows" in the GUI) implement the interface io.ReadWriter. * Support for overlapping views. * The GUI can be modified at runtime (concurrent-safe). * Global and view-level keybindings. * Mouse support. * Colored text. * Customizable edition mode. * Easy to build reusable widgets, complex layouts... ## Installation Execute: ``` $ go get github.com/jroimartin/gocui ``` ## Documentation Execute: ``` $ go doc github.com/jroimartin/gocui ``` Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it online. ## Example ```go package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "Hello world!") } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } ``` ## Screenshots ![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png) ![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png) ![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png) ## Projects using gocui * [komanda-cli](https://github.com/mephux/komanda-cli): IRC Client For Developers. * [vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD. * [wuzz](https://github.com/asciimoo/wuzz): Interactive cli tool for HTTP inspection. * [httplab](https://github.com/gchaincl/httplab): Interactive web server. * [domainr](https://github.com/MichaelThessel/domainr): Tool that checks the availability of domains based on keywords. * [gotime](https://github.com/nanohard/gotime): Time tracker for projects and tasks. * [claws](https://github.com/thehowl/claws): Interactive command line client for testing websockets. * [terminews](http://github.com/antavelos/terminews): Terminal based RSS reader. * [diagram](https://github.com/esimov/diagram): Tool to convert ascii arts into hand drawn diagrams. * [pody](https://github.com/JulienBreux/pody): CLI app to manage Pods in a Kubernetes cluster. * [kubexp](https://github.com/alitari/kubexp): Kubernetes client. * [kcli](https://github.com/cswank/kcli): Tool for inspecting kafka topics/partitions/messages. * [fac](https://github.com/mkchoi212/fac): git merge conflict resolver * [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal. * [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies. Note: if your project is not listed here, let us know! :) gocui-0.4.0/_examples/000077500000000000000000000000001326343017700146045ustar00rootroot00000000000000gocui-0.4.0/_examples/Mark.Twain-Tom.Sawyer.txt000066400000000000000000000346451326343017700213420ustar00rootroot00000000000000The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete by Mark Twain (Samuel Clemens) This eBook is for the use of anyone anywhere at no cost and with almost no restrictions whatsoever. You may copy it, give it away or re-use it under the terms of the Project Gutenberg License included with this eBook or online at www.gutenberg.net Title: The Adventures of Tom Sawyer, Complete Author: Mark Twain (Samuel Clemens) Release Date: August 20, 2006 [EBook #74] [Last updated: May 3, 2011] Language: English *** START OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER *** Produced by David Widger. The previous edition was updated by Jose Menendez. THE ADVENTURES OF TOM SAWYER BY MARK TWAIN (Samuel Langhorne Clemens) P R E F A C E MOST of the adventures recorded in this book really occurred; one or two were experiences of my own, the rest those of boys who were schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but not from an individual--he is a combination of the characteristics of three boys whom I knew, and therefore belongs to the composite order of architecture. The odd superstitions touched upon were all prevalent among children and slaves in the West at the period of this story--that is to say, thirty or forty years ago. Although my book is intended mainly for the entertainment of boys and girls, I hope it will not be shunned by men and women on that account, for part of my plan has been to try to pleasantly remind adults of what they once were themselves, and of how they felt and thought and talked, and what queer enterprises they sometimes engaged in. THE AUTHOR. HARTFORD, 1876. T O M S A W Y E R CHAPTER I "TOM!" No answer. "TOM!" No answer. "What's gone with that boy, I wonder? You TOM!" No answer. The old lady pulled her spectacles down and looked over them about the room; then she put them up and looked out under them. She seldom or never looked THROUGH them for so small a thing as a boy; they were her state pair, the pride of her heart, and were built for "style," not service--she could have seen through a pair of stove-lids just as well. She looked perplexed for a moment, and then said, not fiercely, but still loud enough for the furniture to hear: "Well, I lay if I get hold of you I'll--" She did not finish, for by this time she was bending down and punching under the bed with the broom, and so she needed breath to punctuate the punches with. She resurrected nothing but the cat. "I never did see the beat of that boy!" She went to the open door and stood in it and looked out among the tomato vines and "jimpson" weeds that constituted the garden. No Tom. So she lifted up her voice at an angle calculated for distance and shouted: "Y-o-u-u TOM!" There was a slight noise behind her and she turned just in time to seize a small boy by the slack of his roundabout and arrest his flight. "There! I might 'a' thought of that closet. What you been doing in there?" "Nothing." "Nothing! Look at your hands. And look at your mouth. What IS that truck?" "I don't know, aunt." "Well, I know. It's jam--that's what it is. Forty times I've said if you didn't let that jam alone I'd skin you. Hand me that switch." The switch hovered in the air--the peril was desperate-- "My! Look behind you, aunt!" The old lady whirled round, and snatched her skirts out of danger. The lad fled on the instant, scrambled up the high board-fence, and disappeared over it. His aunt Polly stood surprised a moment, and then broke into a gentle laugh. "Hang the boy, can't I never learn anything? Ain't he played me tricks enough like that for me to be looking out for him by this time? But old fools is the biggest fools there is. Can't learn an old dog new tricks, as the saying is. But my goodness, he never plays them alike, two days, and how is a body to know what's coming? He 'pears to know just how long he can torment me before I get my dander up, and he knows if he can make out to put me off for a minute or make me laugh, it's all down again and I can't hit him a lick. I ain't doing my duty by that boy, and that's the Lord's truth, goodness knows. Spare the rod and spile the child, as the Good Book says. I'm a laying up sin and suffering for us both, I know. He's full of the Old Scratch, but laws-a-me! he's my own dead sister's boy, poor thing, and I ain't got the heart to lash him, somehow. Every time I let him off, my conscience does hurt me so, and every time I hit him my old heart most breaks. Well-a-well, man that is born of woman is of few days and full of trouble, as the Scripture says, and I reckon it's so. He'll play hookey this evening, * and [* Southwestern for "afternoon"] I'll just be obleeged to make him work, to-morrow, to punish him. It's mighty hard to make him work Saturdays, when all the boys is having holiday, but he hates work more than he hates anything else, and I've GOT to do some of my duty by him, or I'll be the ruination of the child." Tom did play hookey, and he had a very good time. He got back home barely in season to help Jim, the small colored boy, saw next-day's wood and split the kindlings before supper--at least he was there in time to tell his adventures to Jim while Jim did three-fourths of the work. Tom's younger brother (or rather half-brother) Sid was already through with his part of the work (picking up chips), for he was a quiet boy, and had no adventurous, troublesome ways. While Tom was eating his supper, and stealing sugar as opportunity offered, Aunt Polly asked him questions that were full of guile, and very deep--for she wanted to trap him into damaging revealments. Like many other simple-hearted souls, it was her pet vanity to believe she was endowed with a talent for dark and mysterious diplomacy, and she loved to contemplate her most transparent devices as marvels of low cunning. Said she: "Tom, it was middling warm in school, warn't it?" "Yes'm." "Powerful warm, warn't it?" "Yes'm." "Didn't you want to go in a-swimming, Tom?" A bit of a scare shot through Tom--a touch of uncomfortable suspicion. He searched Aunt Polly's face, but it told him nothing. So he said: "No'm--well, not very much." The old lady reached out her hand and felt Tom's shirt, and said: "But you ain't too warm now, though." And it flattered her to reflect that she had discovered that the shirt was dry without anybody knowing that that was what she had in her mind. But in spite of her, Tom knew where the wind lay, now. So he forestalled what might be the next move: "Some of us pumped on our heads--mine's damp yet. See?" Aunt Polly was vexed to think she had overlooked that bit of circumstantial evidence, and missed a trick. Then she had a new inspiration: "Tom, you didn't have to undo your shirt collar where I sewed it, to pump on your head, did you? Unbutton your jacket!" The trouble vanished out of Tom's face. He opened his jacket. His shirt collar was securely sewed. "Bother! Well, go 'long with you. I'd made sure you'd played hookey and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a singed cat, as the saying is--better'n you look. THIS time." She was half sorry her sagacity had miscarried, and half glad that Tom had stumbled into obedient conduct for once. But Sidney said: "Well, now, if I didn't think you sewed his collar with white thread, but it's black." "Why, I did sew it with white! Tom!" But Tom did not wait for the rest. As he went out at the door he said: "Siddy, I'll lick you for that." In a safe place Tom examined two large needles which were thrust into the lapels of his jacket, and had thread bound about them--one needle carried white thread and the other black. He said: "She'd never noticed if it hadn't been for Sid. Confound it! sometimes she sews it with white, and sometimes she sews it with black. I wish to geeminy she'd stick to one or t'other--I can't keep the run of 'em. But I bet you I'll lam Sid for that. I'll learn him!" He was not the Model Boy of the village. He knew the model boy very well though--and loathed him. Within two minutes, or even less, he had forgotten all his troubles. Not because his troubles were one whit less heavy and bitter to him than a man's are to a man, but because a new and powerful interest bore them down and drove them out of his mind for the time--just as men's misfortunes are forgotten in the excitement of new enterprises. This new interest was a valued novelty in whistling, which he had just acquired from a negro, and he was suffering to practise it undisturbed. It consisted in a peculiar bird-like turn, a sort of liquid warble, produced by touching the tongue to the roof of the mouth at short intervals in the midst of the music--the reader probably remembers how to do it, if he has ever been a boy. Diligence and attention soon gave him the knack of it, and he strode down the street with his mouth full of harmony and his soul full of gratitude. He felt much as an astronomer feels who has discovered a new planet--no doubt, as far as strong, deep, unalloyed pleasure is concerned, the advantage was with the boy, not the astronomer. The summer evenings were long. It was not dark, yet. Presently Tom checked his whistle. A stranger was before him--a boy a shade larger than himself. A new-comer of any age or either sex was an impressive curiosity in the poor little shabby village of St. Petersburg. This boy was well dressed, too--well dressed on a week-day. This was simply astounding. His cap was a dainty thing, his close-buttoned blue cloth roundabout was new and natty, and so were his pantaloons. He had shoes on--and it was only Friday. He even wore a necktie, a bright bit of ribbon. He had a citified air about him that ate into Tom's vitals. The more Tom stared at the splendid marvel, the higher he turned up his nose at his finery and the shabbier and shabbier his own outfit seemed to him to grow. Neither boy spoke. If one moved, the other moved--but only sidewise, in a circle; they kept face to face and eye to eye all the time. Finally Tom said: "I can lick you!" "I'd like to see you try it." "Well, I can do it." "No you can't, either." "Yes I can." "No you can't." "I can." "You can't." "Can!" "Can't!" An uncomfortable pause. Then Tom said: "What's your name?" "'Tisn't any of your business, maybe." "Well I 'low I'll MAKE it my business." "Well why don't you?" "If you say much, I will." "Much--much--MUCH. There now." "Oh, you think you're mighty smart, DON'T you? I could lick you with one hand tied behind me, if I wanted to." "Well why don't you DO it? You SAY you can do it." "Well I WILL, if you fool with me." "Oh yes--I've seen whole families in the same fix." "Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!" "You can lump that hat if you don't like it. I dare you to knock it off--and anybody that'll take a dare will suck eggs." "You're a liar!" "You're another." "You're a fighting liar and dasn't take it up." "Aw--take a walk!" "Say--if you give me much more of your sass I'll take and bounce a rock off'n your head." "Oh, of COURSE you will." "Well I WILL." "Well why don't you DO it then? What do you keep SAYING you will for? Why don't you DO it? It's because you're afraid." "I AIN'T afraid." "You are." "I ain't." "You are." Another pause, and more eying and sidling around each other. Presently they were shoulder to shoulder. Tom said: "Get away from here!" "Go away yourself!" "I won't." "I won't either." So they stood, each with a foot placed at an angle as a brace, and both shoving with might and main, and glowering at each other with hate. But neither could get an advantage. After struggling till both were hot and flushed, each relaxed his strain with watchful caution, and Tom said: "You're a coward and a pup. I'll tell my big brother on you, and he can thrash you with his little finger, and I'll make him do it, too." "What do I care for your big brother? I've got a brother that's bigger than he is--and what's more, he can throw him over that fence, too." [Both brothers were imaginary.] "That's a lie." "YOUR saying so don't make it so." Tom drew a line in the dust with his big toe, and said: "I dare you to step over that, and I'll lick you till you can't stand up. Anybody that'll take a dare will steal sheep." The new boy stepped over promptly, and said: "Now you said you'd do it, now let's see you do it." "Don't you crowd me now; you better look out." "Well, you SAID you'd do it--why don't you do it?" "By jingo! for two cents I WILL do it." The new boy took two broad coppers out of his pocket and held them out with derision. Tom struck them to the ground. In an instant both boys were rolling and tumbling in the dirt, gripped together like cats; and for the space of a minute they tugged and tore at each other's hair and clothes, punched and scratched each other's nose, and covered themselves with dust and glory. Presently the confusion took form, and through the fog of battle Tom appeared, seated astride the new boy, and pounding him with his fists. "Holler 'nuff!" said he. The boy only struggled to free himself. He was crying--mainly from rage. "Holler 'nuff!"--and the pounding went on. At last the stranger got out a smothered "'Nuff!" and Tom let him up and said: "Now that'll learn you. Better look out who you're fooling with next time." The new boy went off brushing the dust from his clothes, sobbing, snuffling, and occasionally looking back and shaking his head and threatening what he would do to Tom the "next time he caught him out." To which Tom responded with jeers, and started off in high feather, and as soon as his back was turned the new boy snatched up a stone, threw it and hit him between the shoulders and then turned tail and ran like an antelope. Tom chased the traitor home, and thus found out where he lived. He then held a position at the gate for some time, daring the enemy to come outside, but the enemy only made faces at him through the window and declined. At last the enemy's mother appeared, and called Tom a bad, vicious, vulgar child, and ordered him away. So he went away; but he said he "'lowed" to "lay" for that boy. He got home pretty late that night, and when he climbed cautiously in at the window, he uncovered an ambuscade, in the person of his aunt; and when she saw the state his clothes were in her resolution to turn his Saturday holiday into captivity at hard labor became adamantine in its firmness. gocui-0.4.0/_examples/active.go000066400000000000000000000046171326343017700164160ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) var ( viewArr = []string{"v1", "v2", "v3", "v4"} active = 0 ) func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) { if _, err := g.SetCurrentView(name); err != nil { return nil, err } return g.SetViewOnTop(name) } func nextView(g *gocui.Gui, v *gocui.View) error { nextIndex := (active + 1) % len(viewArr) name := viewArr[nextIndex] out, err := g.View("v2") if err != nil { return err } fmt.Fprintln(out, "Going from view "+v.Name()+" to "+name) if _, err := setCurrentViewOnTop(g, name); err != nil { return err } if nextIndex == 0 || nextIndex == 3 { g.Cursor = true } else { g.Cursor = false } active = nextIndex return nil } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("v1", 0, 0, maxX/2-1, maxY/2-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "v1 (editable)" v.Editable = true v.Wrap = true if _, err = setCurrentViewOnTop(g, "v1"); err != nil { return err } } if v, err := g.SetView("v2", maxX/2-1, 0, maxX-1, maxY/2-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "v2" v.Wrap = true v.Autoscroll = true } if v, err := g.SetView("v3", 0, maxY/2-1, maxX/2-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "v3" v.Wrap = true v.Autoscroll = true fmt.Fprint(v, "Press TAB to change current view") } if v, err := g.SetView("v4", maxX/2, maxY/2, maxX-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "v4 (editable)" v.Editable = true } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.Highlight = true g.Cursor = true g.SelFgColor = gocui.ColorGreen g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } gocui-0.4.0/_examples/bufs.go000066400000000000000000000025501326343017700160740ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // WARNING: tricky code just for testing purposes, do not use as reference. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) var vbuf, buf string func quit(g *gocui.Gui, v *gocui.View) error { vbuf = v.ViewBuffer() buf = v.Buffer() return gocui.ErrQuit } func overwrite(g *gocui.Gui, v *gocui.View) error { v.Overwrite = !v.Overwrite return nil } func layout(g *gocui.Gui) error { _, maxY := g.Size() if v, err := g.SetView("main", 0, 0, 20, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Editable = true v.Wrap = true if _, err := g.SetCurrentView("main"); err != nil { return err } } return nil } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } g.Cursor = true g.Mouse = true g.SetManagerFunc(layout) if err := g.SetKeybinding("main", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.SetKeybinding("main", gocui.KeyCtrlI, gocui.ModNone, overwrite); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } g.Close() fmt.Printf("VBUF:\n%s\n", vbuf) fmt.Printf("BUF:\n%s\n", buf) } gocui-0.4.0/_examples/colors.go000066400000000000000000000017451326343017700164430ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("colors", maxX/2-7, maxY/2-12, maxX/2+7, maxY/2+13); err != nil { if err != gocui.ErrUnknownView { return err } for i := 0; i <= 7; i++ { for _, j := range []int{1, 4, 7} { fmt.Fprintf(v, "Hello \033[3%d;%dmcolors!\033[0m\n", i, j) } } } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } gocui-0.4.0/_examples/colors256.go000066400000000000000000000025331326343017700166740ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.Output256) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("colors", -1, -1, maxX, maxY); err != nil { if err != gocui.ErrUnknownView { return err } // 256-colors escape codes for i := 0; i < 256; i++ { str := fmt.Sprintf("\x1b[48;5;%dm\x1b[30m%3d\x1b[0m ", i, i) str += fmt.Sprintf("\x1b[38;5;%dm%3d\x1b[0m ", i, i) if (i+1)%10 == 0 { str += "\n" } fmt.Fprint(v, str) } fmt.Fprint(v, "\n\n") // 8-colors escape codes ctr := 0 for i := 0; i <= 7; i++ { for _, j := range []int{1, 4, 7} { str := fmt.Sprintf("\x1b[3%d;%dm%d:%d\x1b[0m ", i, j, i, j) if (ctr+1)%20 == 0 { str += "\n" } fmt.Fprint(v, str) ctr++ } } } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } gocui-0.4.0/_examples/demo.go000066400000000000000000000105131326343017700160570ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "io" "io/ioutil" "log" "strings" "github.com/jroimartin/gocui" ) func nextView(g *gocui.Gui, v *gocui.View) error { if v == nil || v.Name() == "side" { _, err := g.SetCurrentView("main") return err } _, err := g.SetCurrentView("side") return err } func cursorDown(g *gocui.Gui, v *gocui.View) error { if v != nil { cx, cy := v.Cursor() if err := v.SetCursor(cx, cy+1); err != nil { ox, oy := v.Origin() if err := v.SetOrigin(ox, oy+1); err != nil { return err } } } return nil } func cursorUp(g *gocui.Gui, v *gocui.View) error { if v != nil { ox, oy := v.Origin() cx, cy := v.Cursor() if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 { if err := v.SetOrigin(ox, oy-1); err != nil { return err } } } return nil } func getLine(g *gocui.Gui, v *gocui.View) error { var l string var err error _, cy := v.Cursor() if l, err = v.Line(cy); err != nil { l = "" } maxX, maxY := g.Size() if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, l) if _, err := g.SetCurrentView("msg"); err != nil { return err } } return nil } func delMsg(g *gocui.Gui, v *gocui.View) error { if err := g.DeleteView("msg"); err != nil { return err } if _, err := g.SetCurrentView("side"); err != nil { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func keybindings(g *gocui.Gui) error { if err := g.SetKeybinding("side", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil { return err } if err := g.SetKeybinding("main", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil { return err } if err := g.SetKeybinding("side", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil { return err } if err := g.SetKeybinding("side", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } if err := g.SetKeybinding("side", gocui.KeyEnter, gocui.ModNone, getLine); err != nil { return err } if err := g.SetKeybinding("msg", gocui.KeyEnter, gocui.ModNone, delMsg); err != nil { return err } if err := g.SetKeybinding("main", gocui.KeyCtrlS, gocui.ModNone, saveMain); err != nil { return err } if err := g.SetKeybinding("main", gocui.KeyCtrlW, gocui.ModNone, saveVisualMain); err != nil { return err } return nil } func saveMain(g *gocui.Gui, v *gocui.View) error { f, err := ioutil.TempFile("", "gocui_demo_") if err != nil { return err } defer f.Close() p := make([]byte, 5) v.Rewind() for { n, err := v.Read(p) if n > 0 { if _, err := f.Write(p[:n]); err != nil { return err } } if err == io.EOF { break } if err != nil { return err } } return nil } func saveVisualMain(g *gocui.Gui, v *gocui.View) error { f, err := ioutil.TempFile("", "gocui_demo_") if err != nil { return err } defer f.Close() vb := v.ViewBuffer() if _, err := io.Copy(f, strings.NewReader(vb)); err != nil { return err } return nil } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil { if err != gocui.ErrUnknownView { return err } v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack fmt.Fprintln(v, "Item 1") fmt.Fprintln(v, "Item 2") fmt.Fprintln(v, "Item 3") fmt.Fprint(v, "\rWill be") fmt.Fprint(v, "deleted\rItem 4\nItem 5") } if v, err := g.SetView("main", 30, -1, maxX, maxY); err != nil { if err != gocui.ErrUnknownView { return err } b, err := ioutil.ReadFile("Mark.Twain-Tom.Sawyer.txt") if err != nil { panic(err) } fmt.Fprintf(v, "%s", b) v.Editable = true v.Wrap = true if _, err := g.SetCurrentView("main"); err != nil { return err } } return nil } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.Cursor = true g.SetManagerFunc(layout) if err := keybindings(g); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } gocui-0.4.0/_examples/dynamic.go000066400000000000000000000101741326343017700165620ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "strings" "github.com/jroimartin/gocui" ) const delta = 1 var ( views = []string{} curView = -1 idxView = 0 ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.Highlight = true g.SelFgColor = gocui.ColorRed g.SetManagerFunc(layout) if err := initKeybindings(g); err != nil { log.Panicln(err) } if err := newView(g); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, _ := g.Size() v, err := g.SetView("help", maxX-25, 0, maxX-1, 9) if err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "KEYBINDINGS") fmt.Fprintln(v, "Space: New View") fmt.Fprintln(v, "Tab: Next View") fmt.Fprintln(v, "← ↑ → ↓: Move View") fmt.Fprintln(v, "Backspace: Delete View") fmt.Fprintln(v, "t: Set view on top") fmt.Fprintln(v, "b: Set view on bottom") fmt.Fprintln(v, "^C: Exit") } return nil } func initKeybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return newView(g) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return delView(g) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return nextView(g, true) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return moveView(g, v, -delta, 0) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return moveView(g, v, delta, 0) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return moveView(g, v, 0, delta) }); err != nil { return err } if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return moveView(g, v, 0, -delta) }); err != nil { return err } if err := g.SetKeybinding("", 't', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { _, err := g.SetViewOnTop(views[curView]) return err }); err != nil { return err } if err := g.SetKeybinding("", 'b', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { _, err := g.SetViewOnBottom(views[curView]) return err }); err != nil { return err } return nil } func newView(g *gocui.Gui) error { maxX, maxY := g.Size() name := fmt.Sprintf("v%v", idxView) v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5) if err != nil { if err != gocui.ErrUnknownView { return err } v.Wrap = true fmt.Fprintln(v, strings.Repeat(name+" ", 30)) } if _, err := g.SetCurrentView(name); err != nil { return err } views = append(views, name) curView = len(views) - 1 idxView += 1 return nil } func delView(g *gocui.Gui) error { if len(views) <= 1 { return nil } if err := g.DeleteView(views[curView]); err != nil { return err } views = append(views[:curView], views[curView+1:]...) return nextView(g, false) } func nextView(g *gocui.Gui, disableCurrent bool) error { next := curView + 1 if next > len(views)-1 { next = 0 } if _, err := g.SetCurrentView(views[next]); err != nil { return err } curView = next return nil } func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error { name := v.Name() x0, y0, x1, y1, err := g.ViewPosition(name) if err != nil { return err } if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy); err != nil { return err } return nil } gocui-0.4.0/_examples/flow_layout.go000066400000000000000000000031221326343017700174750ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "strings" "github.com/jroimartin/gocui" ) type Label struct { name string w, h int body string } func NewLabel(name string, body string) *Label { lines := strings.Split(body, "\n") w := 0 for _, l := range lines { if len(l) > w { w = len(l) } } h := len(lines) + 1 w = w + 1 return &Label{name: name, w: w, h: h, body: body} } func (w *Label) Layout(g *gocui.Gui) error { v, err := g.SetView(w.name, 0, 0, w.w, w.h) if err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprint(v, w.body) } return nil } func flowLayout(g *gocui.Gui) error { views := g.Views() x := 0 for _, v := range views { w, h := v.Size() _, err := g.SetView(v.Name(), x, 0, x+w+1, h+1) if err != nil && err != gocui.ErrUnknownView { return err } x += w + 2 } return nil } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() l1 := NewLabel("l1", "This") l2 := NewLabel("l2", "is") l3 := NewLabel("l3", "a") l4 := NewLabel("l4", "flow\nlayout") l5 := NewLabel("l5", "!") fl := gocui.ManagerFunc(flowLayout) g.SetManager(l1, l2, l3, l4, l5, fl) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } gocui-0.4.0/_examples/goroutine.go000066400000000000000000000027771326343017700171630ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "sync" "time" "github.com/jroimartin/gocui" ) const NumGoroutines = 10 var ( done = make(chan struct{}) wg sync.WaitGroup mu sync.Mutex // protects ctr ctr = 0 ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := keybindings(g); err != nil { log.Panicln(err) } for i := 0; i < NumGoroutines; i++ { wg.Add(1) go counter(g) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } wg.Wait() } func layout(g *gocui.Gui) error { if v, err := g.SetView("ctr", 2, 2, 12, 4); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "0") } return nil } func keybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { close(done) return gocui.ErrQuit } func counter(g *gocui.Gui) { defer wg.Done() for { select { case <-done: return case <-time.After(500 * time.Millisecond): mu.Lock() n := ctr ctr++ mu.Unlock() g.Update(func(g *gocui.Gui) error { v, err := g.View("ctr") if err != nil { return err } v.Clear() fmt.Fprintln(v, n) return nil }) } } } gocui-0.4.0/_examples/hello.go000066400000000000000000000015701326343017700162410ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "Hello world!") } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } gocui-0.4.0/_examples/layout.go000066400000000000000000000021141326343017700164460ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "log" "github.com/jroimartin/gocui" ) func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if _, err := g.SetView("side", -1, -1, int(0.2*float32(maxX)), maxY-5); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("main", int(0.2*float32(maxX)), -1, maxX, maxY-5); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("cmdline", -1, maxY-5, maxX, maxY); err != nil && err != gocui.ErrUnknownView { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } gocui-0.4.0/_examples/mask.go000066400000000000000000000027021326343017700160670ustar00rootroot00000000000000// Copyright 2015 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Fatalln(err) } defer g.Close() g.Cursor = true g.SetManagerFunc(layout) if err := initKeybindings(g); err != nil { log.Fatalln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Fatalln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("help", maxX-23, 0, maxX-1, 3); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Keybindings" fmt.Fprintln(v, "^a: Set mask") fmt.Fprintln(v, "^c: Exit") } if v, err := g.SetView("input", 0, 0, maxX-24, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } if _, err := g.SetCurrentView("input"); err != nil { return err } v.Editable = true v.Wrap = true } return nil } func initKeybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit }); err != nil { return err } if err := g.SetKeybinding("input", gocui.KeyCtrlA, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { v.Mask ^= '*' return nil }); err != nil { return err } return nil } gocui-0.4.0/_examples/mouse.go000066400000000000000000000043041326343017700162640ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.Cursor = true g.Mouse = true g.SetManagerFunc(layout) if err := keybindings(g); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { if v, err := g.SetView("but1", 2, 2, 22, 7); err != nil { if err != gocui.ErrUnknownView { return err } v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack fmt.Fprintln(v, "Button 1 - line 1") fmt.Fprintln(v, "Button 1 - line 2") fmt.Fprintln(v, "Button 1 - line 3") fmt.Fprintln(v, "Button 1 - line 4") } if v, err := g.SetView("but2", 24, 2, 44, 4); err != nil { if err != gocui.ErrUnknownView { return err } v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack fmt.Fprintln(v, "Button 2 - line 1") } return nil } func keybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } for _, n := range []string{"but1", "but2"} { if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil { return err } } if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, delMsg); err != nil { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func showMsg(g *gocui.Gui, v *gocui.View) error { var l string var err error if _, err := g.SetCurrentView(v.Name()); err != nil { return err } _, cy := v.Cursor() if l, err = v.Line(cy); err != nil { l = "" } maxX, maxY := g.Size() if v, err := g.SetView("msg", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, l) } return nil } func delMsg(g *gocui.Gui, v *gocui.View) error { if err := g.DeleteView("msg"); err != nil { return err } return nil } gocui-0.4.0/_examples/ontop.go000066400000000000000000000033151326343017700162740ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := keybindings(g); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { if v, err := g.SetView("v1", 10, 2, 30, 6); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "View #1") } if v, err := g.SetView("v2", 20, 4, 40, 8); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "View #2") } if v, err := g.SetView("v3", 30, 6, 50, 10); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "View #3") } return nil } func keybindings(g *gocui.Gui) error { err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit }) if err != nil { return err } err = g.SetKeybinding("", '1', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { _, err := g.SetViewOnTop("v1") return err }) if err != nil { return err } err = g.SetKeybinding("", '2', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { _, err := g.SetViewOnTop("v2") return err }) if err != nil { return err } err = g.SetKeybinding("", '3', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { _, err := g.SetViewOnTop("v3") return err }) if err != nil { return err } return nil } gocui-0.4.0/_examples/overlap.go000066400000000000000000000034111326343017700166020ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "log" "github.com/jroimartin/gocui" ) func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if _, err := g.SetView("v1", -1, -1, 10, 10); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v2", maxX-10, -1, maxX, 10); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v3", maxX/2-5, -1, maxX/2+5, 10); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v4", -1, maxY/2-5, 10, maxY/2+5); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v5", maxX-10, maxY/2-5, maxX, maxY/2+5); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v6", -1, maxY-10, 10, maxY); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v7", maxX-10, maxY-10, maxX, maxY); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v8", maxX/2-5, maxY-10, maxX/2+5, maxY); err != nil && err != gocui.ErrUnknownView { return err } if _, err := g.SetView("v9", maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5); err != nil && err != gocui.ErrUnknownView { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } gocui-0.4.0/_examples/size.go000066400000000000000000000015751326343017700161150ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() v, err := g.SetView("size", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2) if err != nil && err != gocui.ErrUnknownView { return err } v.Clear() fmt.Fprintf(v, "%d, %d", maxX, maxY) return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } gocui-0.4.0/_examples/stdin.go000066400000000000000000000042301326343017700162530ustar00rootroot00000000000000// Copyright 2015 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "encoding/hex" "fmt" "io" "log" "os" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Fatalln(err) } defer g.Close() g.Cursor = true g.SetManagerFunc(layout) if err := initKeybindings(g); err != nil { log.Fatalln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Fatalln(err) } } func layout(g *gocui.Gui) error { maxX, _ := g.Size() if v, err := g.SetView("help", maxX-23, 0, maxX-1, 5); err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprintln(v, "KEYBINDINGS") fmt.Fprintln(v, "↑ ↓: Seek input") fmt.Fprintln(v, "a: Enable autoscroll") fmt.Fprintln(v, "^C: Exit") } if v, err := g.SetView("stdin", 0, 0, 80, 35); err != nil { if err != gocui.ErrUnknownView { return err } if _, err := g.SetCurrentView("stdin"); err != nil { return err } dumper := hex.Dumper(v) if _, err := io.Copy(dumper, os.Stdin); err != nil { return err } v.Wrap = true } return nil } func initKeybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } if err := g.SetKeybinding("stdin", 'a', gocui.ModNone, autoscroll); err != nil { return err } if err := g.SetKeybinding("stdin", gocui.KeyArrowUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { scrollView(v, -1) return nil }); err != nil { return err } if err := g.SetKeybinding("stdin", gocui.KeyArrowDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { scrollView(v, 1) return nil }); err != nil { return err } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func autoscroll(g *gocui.Gui, v *gocui.View) error { v.Autoscroll = true return nil } func scrollView(v *gocui.View, dy int) error { if v != nil { v.Autoscroll = false ox, oy := v.Origin() if err := v.SetOrigin(ox, oy+dy); err != nil { return err } } return nil } gocui-0.4.0/_examples/title.go000066400000000000000000000065621326343017700162650ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "log" "github.com/jroimartin/gocui" ) func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() // Overlap (front) if v, err := g.SetView("v1", 10, 2, 30, 6); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v2", 20, 4, 40, 8); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } // Overlap (back) if v, err := g.SetView("v3", 60, 4, 80, 8); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v4", 50, 2, 70, 6); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } // Overlap (frame) if v, err := g.SetView("v15", 90, 2, 110, 5); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v16", 100, 5, 120, 8); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v17", 140, 5, 160, 8); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v18", 130, 2, 150, 5); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } // Long title if v, err := g.SetView("v5", 10, 12, 30, 16); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Long long long long title" } // No title if v, err := g.SetView("v6", 35, 12, 55, 16); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "" } if _, err := g.SetView("v7", 60, 12, 80, 16); err != nil { if err != gocui.ErrUnknownView { return err } } // Small view if v, err := g.SetView("v8", 85, 12, 88, 16); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } // Screen borders if v, err := g.SetView("v9", -10, 20, 10, 24); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v10", maxX-10, 20, maxX+10, 24); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } // Out of screen if v, err := g.SetView("v11", -21, 28, -1, 32); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v12", maxX, 28, maxX+20, 32); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v13", 10, -7, 30, -1); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } if v, err := g.SetView("v14", 10, maxY, 30, maxY+6); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Regular title" } return nil } gocui-0.4.0/_examples/widgets.go000066400000000000000000000074761326343017700166170ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "errors" "fmt" "log" "strings" "github.com/jroimartin/gocui" ) const delta = 0.2 type HelpWidget struct { name string x, y int w, h int body string } func NewHelpWidget(name string, x, y int, body string) *HelpWidget { lines := strings.Split(body, "\n") w := 0 for _, l := range lines { if len(l) > w { w = len(l) } } h := len(lines) + 1 w = w + 1 return &HelpWidget{name: name, x: x, y: y, w: w, h: h, body: body} } func (w *HelpWidget) Layout(g *gocui.Gui) error { v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+w.h) if err != nil { if err != gocui.ErrUnknownView { return err } fmt.Fprint(v, w.body) } return nil } type StatusbarWidget struct { name string x, y int w int val float64 } func NewStatusbarWidget(name string, x, y, w int) *StatusbarWidget { return &StatusbarWidget{name: name, x: x, y: y, w: w} } func (w *StatusbarWidget) SetVal(val float64) error { if val < 0 || val > 1 { return errors.New("invalid value") } w.val = val return nil } func (w *StatusbarWidget) Val() float64 { return w.val } func (w *StatusbarWidget) Layout(g *gocui.Gui) error { v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+2) if err != nil && err != gocui.ErrUnknownView { return err } v.Clear() rep := int(w.val * float64(w.w-1)) fmt.Fprint(v, strings.Repeat("▒", rep)) return nil } type ButtonWidget struct { name string x, y int w int label string handler func(g *gocui.Gui, v *gocui.View) error } func NewButtonWidget(name string, x, y int, label string, handler func(g *gocui.Gui, v *gocui.View) error) *ButtonWidget { return &ButtonWidget{name: name, x: x, y: y, w: len(label) + 1, label: label, handler: handler} } func (w *ButtonWidget) Layout(g *gocui.Gui) error { v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+2) if err != nil { if err != gocui.ErrUnknownView { return err } if _, err := g.SetCurrentView(w.name); err != nil { return err } if err := g.SetKeybinding(w.name, gocui.KeyEnter, gocui.ModNone, w.handler); err != nil { return err } fmt.Fprint(v, w.label) } return nil } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.Highlight = true g.SelFgColor = gocui.ColorRed help := NewHelpWidget("help", 1, 1, helpText) status := NewStatusbarWidget("status", 1, 7, 50) butdown := NewButtonWidget("butdown", 52, 7, "DOWN", statusDown(status)) butup := NewButtonWidget("butup", 58, 7, "UP", statusUp(status)) g.SetManager(help, status, butdown, butup) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, toggleButton); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func toggleButton(g *gocui.Gui, v *gocui.View) error { nextview := "butdown" if v != nil && v.Name() == "butdown" { nextview = "butup" } _, err := g.SetCurrentView(nextview) return err } func statusUp(status *StatusbarWidget) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { return statusSet(status, delta) } } func statusDown(status *StatusbarWidget) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { return statusSet(status, -delta) } } func statusSet(sw *StatusbarWidget, inc float64) error { val := sw.Val() + inc if val < 0 || val > 1 { return nil } return sw.SetVal(val) } const helpText = `KEYBINDINGS Tab: Move between buttons Enter: Push button ^C: Exit` gocui-0.4.0/_examples/wrap.go000066400000000000000000000017251326343017700161110ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "log" "strings" "github.com/jroimartin/gocui" ) func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if v, err := g.SetView("main", 1, 1, maxX-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } v.Wrap = true line := strings.Repeat("This is a long line -- ", 10) fmt.Fprintf(v, "%s\n\n", line) fmt.Fprintln(v, "Short") } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func main() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } gocui-0.4.0/attribute.go000066400000000000000000000022411326343017700151600ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import "github.com/nsf/termbox-go" // Attribute represents a terminal attribute, like color, font style, etc. They // can be combined using bitwise OR (|). Note that it is not possible to // combine multiple color attributes. type Attribute termbox.Attribute // Color attributes. const ( ColorDefault Attribute = Attribute(termbox.ColorDefault) ColorBlack = Attribute(termbox.ColorBlack) ColorRed = Attribute(termbox.ColorRed) ColorGreen = Attribute(termbox.ColorGreen) ColorYellow = Attribute(termbox.ColorYellow) ColorBlue = Attribute(termbox.ColorBlue) ColorMagenta = Attribute(termbox.ColorMagenta) ColorCyan = Attribute(termbox.ColorCyan) ColorWhite = Attribute(termbox.ColorWhite) ) // Text style attributes. const ( AttrBold Attribute = Attribute(termbox.AttrBold) AttrUnderline = Attribute(termbox.AttrUnderline) AttrReverse = Attribute(termbox.AttrReverse) ) gocui-0.4.0/doc.go000066400000000000000000000063261326343017700137320ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package gocui allows to create console user interfaces. Create a new GUI: g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { // handle error } defer g.Close() // Set GUI managers and key bindings // ... if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { // handle error } Set GUI managers: g.SetManager(mgr1, mgr2) Managers are in charge of GUI's layout and can be used to build widgets. On each iteration of the GUI's main loop, the Layout function of each configured manager is executed. Managers are used to set-up and update the application's main views, being possible to freely change them during execution. Also, it is important to mention that a main loop iteration is executed on each reported event (key-press, mouse event, window resize, etc). GUIs are composed by Views, you can think of it as buffers. Views implement the io.ReadWriter interface, so you can just write to them if you want to modify their content. The same is valid for reading. Create and initialize a view with absolute coordinates: if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil { if err != gocui.ErrUnknownView { // handle error } fmt.Fprintln(v, "This is a new view") // ... } Views can also be created using relative coordinates: maxX, maxY := g.Size() if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil { // ... } Configure keybindings: if err := g.SetKeybinding("viewname", gocui.KeyEnter, gocui.ModNone, fcn); err != nil { // handle error } gocui implements full mouse support that can be enabled with: g.Mouse = true Mouse events are handled like any other keybinding: if err := g.SetKeybinding("viewname", gocui.MouseLeft, gocui.ModNone, fcn); err != nil { // handle error } IMPORTANT: Views can only be created, destroyed or updated in three ways: from the Layout function within managers, from keybinding callbacks or via *Gui.Update(). The reason for this is that it allows gocui to be concurrent-safe. So, if you want to update your GUI from a goroutine, you must use *Gui.Update(). For example: g.Update(func(g *gocui.Gui) error { v, err := g.View("viewname") if err != nil { // handle error } v.Clear() fmt.Fprintln(v, "Writing from different goroutines") return nil }) By default, gocui provides a basic edition mode. This mode can be extended and customized creating a new Editor and assigning it to *View.Editor: type Editor interface { Edit(v *View, key Key, ch rune, mod Modifier) } DefaultEditor can be taken as example to create your own custom Editor: var DefaultEditor Editor = EditorFunc(simpleEditor) func simpleEditor(v *View, key Key, ch rune, mod Modifier) { switch { case ch != 0 && mod == 0: v.EditWrite(ch) case key == KeySpace: v.EditWrite(' ') case key == KeyBackspace || key == KeyBackspace2: v.EditDelete(true) // ... } } Colored text: Views allow to add colored text using ANSI colors. For example: fmt.Fprintln(v, "\x1b[0;31mHello world") For more information, see the examples in folder "_examples/". */ package gocui gocui-0.4.0/edit.go000066400000000000000000000167371326343017700141210ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import "errors" const maxInt = int(^uint(0) >> 1) // Editor interface must be satisfied by gocui editors. type Editor interface { Edit(v *View, key Key, ch rune, mod Modifier) } // The EditorFunc type is an adapter to allow the use of ordinary functions as // Editors. If f is a function with the appropriate signature, EditorFunc(f) // is an Editor object that calls f. type EditorFunc func(v *View, key Key, ch rune, mod Modifier) // Edit calls f(v, key, ch, mod) func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) { f(v, key, ch, mod) } // DefaultEditor is the default editor. var DefaultEditor Editor = EditorFunc(simpleEditor) // simpleEditor is used as the default gocui editor. func simpleEditor(v *View, key Key, ch rune, mod Modifier) { switch { case ch != 0 && mod == 0: v.EditWrite(ch) case key == KeySpace: v.EditWrite(' ') case key == KeyBackspace || key == KeyBackspace2: v.EditDelete(true) case key == KeyDelete: v.EditDelete(false) case key == KeyInsert: v.Overwrite = !v.Overwrite case key == KeyEnter: v.EditNewLine() case key == KeyArrowDown: v.MoveCursor(0, 1, false) case key == KeyArrowUp: v.MoveCursor(0, -1, false) case key == KeyArrowLeft: v.MoveCursor(-1, 0, false) case key == KeyArrowRight: v.MoveCursor(1, 0, false) } } // EditWrite writes a rune at the cursor position. func (v *View) EditWrite(ch rune) { v.writeRune(v.cx, v.cy, ch) v.MoveCursor(1, 0, true) } // EditDelete deletes a rune at the cursor position. back determines the // direction. func (v *View) EditDelete(back bool) { x, y := v.ox+v.cx, v.oy+v.cy if y < 0 { return } else if y >= len(v.viewLines) { v.MoveCursor(-1, 0, true) return } maxX, _ := v.Size() if back { if x == 0 { // start of the line if y < 1 { return } var maxPrevWidth int if v.Wrap { maxPrevWidth = maxX } else { maxPrevWidth = maxInt } if v.viewLines[y].linesX == 0 { // regular line v.mergeLines(v.cy - 1) if len(v.viewLines[y-1].line) < maxPrevWidth { v.MoveCursor(-1, 0, true) } } else { // wrapped line v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1) v.MoveCursor(-1, 0, true) } } else { // middle/end of the line v.deleteRune(v.cx-1, v.cy) v.MoveCursor(-1, 0, true) } } else { if x == len(v.viewLines[y].line) { // end of the line v.mergeLines(v.cy) } else { // start/middle of the line v.deleteRune(v.cx, v.cy) } } } // EditNewLine inserts a new line under the cursor. func (v *View) EditNewLine() { v.breakLine(v.cx, v.cy) v.ox = 0 v.cx = 0 v.MoveCursor(0, 1, true) } // MoveCursor moves the cursor taking into account the width of the line/view, // displacing the origin if necessary. func (v *View) MoveCursor(dx, dy int, writeMode bool) { maxX, maxY := v.Size() cx, cy := v.cx+dx, v.cy+dy x, y := v.ox+cx, v.oy+cy var curLineWidth, prevLineWidth int // get the width of the current line if writeMode { if v.Wrap { curLineWidth = maxX - 1 } else { curLineWidth = maxInt } } else { if y >= 0 && y < len(v.viewLines) { curLineWidth = len(v.viewLines[y].line) if v.Wrap && curLineWidth >= maxX { curLineWidth = maxX - 1 } } else { curLineWidth = 0 } } // get the width of the previous line if y-1 >= 0 && y-1 < len(v.viewLines) { prevLineWidth = len(v.viewLines[y-1].line) } else { prevLineWidth = 0 } // adjust cursor's x position and view's x origin if x > curLineWidth { // move to next line if dx > 0 { // horizontal movement cy++ if writeMode || v.oy+cy < len(v.viewLines) { if !v.Wrap { v.ox = 0 } v.cx = 0 } } else { // vertical movement if curLineWidth > 0 { // move cursor to the EOL if v.Wrap { v.cx = curLineWidth } else { ncx := curLineWidth - v.ox if ncx < 0 { v.ox += ncx if v.ox < 0 { v.ox = 0 } v.cx = 0 } else { v.cx = ncx } } } else { if writeMode || v.oy+cy < len(v.viewLines) { if !v.Wrap { v.ox = 0 } v.cx = 0 } } } } else if cx < 0 { if !v.Wrap && v.ox > 0 { // move origin to the left v.ox += cx v.cx = 0 } else { // move to previous line cy-- if prevLineWidth > 0 { if !v.Wrap { // set origin so the EOL is visible nox := prevLineWidth - maxX + 1 if nox < 0 { v.ox = 0 } else { v.ox = nox } } v.cx = prevLineWidth } else { if !v.Wrap { v.ox = 0 } v.cx = 0 } } } else { // stay on the same line if v.Wrap { v.cx = cx } else { if cx >= maxX { v.ox += cx - maxX + 1 v.cx = maxX } else { v.cx = cx } } } // adjust cursor's y position and view's y origin if cy < 0 { if v.oy > 0 { v.oy-- } } else if writeMode || v.oy+cy < len(v.viewLines) { if cy >= maxY { v.oy++ } else { v.cy = cy } } } // writeRune writes a rune into the view's internal buffer, at the // position corresponding to the point (x, y). The length of the internal // buffer is increased if the point is out of bounds. Overwrite mode is // governed by the value of View.overwrite. func (v *View) writeRune(x, y int, ch rune) error { v.tainted = true x, y, err := v.realPosition(x, y) if err != nil { return err } if x < 0 || y < 0 { return errors.New("invalid point") } if y >= len(v.lines) { s := make([][]cell, y-len(v.lines)+1) v.lines = append(v.lines, s...) } olen := len(v.lines[y]) var s []cell if x >= len(v.lines[y]) { s = make([]cell, x-len(v.lines[y])+1) } else if !v.Overwrite { s = make([]cell, 1) } v.lines[y] = append(v.lines[y], s...) if !v.Overwrite || (v.Overwrite && x >= olen-1) { copy(v.lines[y][x+1:], v.lines[y][x:]) } v.lines[y][x] = cell{ fgColor: v.FgColor, bgColor: v.BgColor, chr: ch, } return nil } // deleteRune removes a rune from the view's internal buffer, at the // position corresponding to the point (x, y). func (v *View) deleteRune(x, y int) error { v.tainted = true x, y, err := v.realPosition(x, y) if err != nil { return err } if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) { return errors.New("invalid point") } v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...) return nil } // mergeLines merges the lines "y" and "y+1" if possible. func (v *View) mergeLines(y int) error { v.tainted = true _, y, err := v.realPosition(0, y) if err != nil { return err } if y < 0 || y >= len(v.lines) { return errors.New("invalid point") } if y < len(v.lines)-1 { // otherwise we don't need to merge anything v.lines[y] = append(v.lines[y], v.lines[y+1]...) v.lines = append(v.lines[:y+1], v.lines[y+2:]...) } return nil } // breakLine breaks a line of the internal buffer at the position corresponding // to the point (x, y). func (v *View) breakLine(x, y int) error { v.tainted = true x, y, err := v.realPosition(x, y) if err != nil { return err } if y < 0 || y >= len(v.lines) { return errors.New("invalid point") } var left, right []cell if x < len(v.lines[y]) { // break line left = make([]cell, len(v.lines[y][:x])) copy(left, v.lines[y][:x]) right = make([]cell, len(v.lines[y][x:])) copy(right, v.lines[y][x:]) } else { // new empty line left = v.lines[y] } lines := make([][]cell, len(v.lines)+1) lines[y] = left lines[y+1] = right copy(lines, v.lines[:y]) copy(lines[y+2:], v.lines[y+1:]) v.lines = lines return nil } gocui-0.4.0/escape.go000066400000000000000000000117551326343017700144270ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import ( "errors" "strconv" ) type escapeInterpreter struct { state escapeState curch rune csiParam []string curFgColor, curBgColor Attribute mode OutputMode } type escapeState int const ( stateNone escapeState = iota stateEscape stateCSI stateParams ) var ( errNotCSI = errors.New("Not a CSI escape sequence") errCSIParseError = errors.New("CSI escape sequence parsing error") errCSITooLong = errors.New("CSI escape sequence is too long") ) // runes in case of error will output the non-parsed runes as a string. func (ei *escapeInterpreter) runes() []rune { switch ei.state { case stateNone: return []rune{0x1b} case stateEscape: return []rune{0x1b, ei.curch} case stateCSI: return []rune{0x1b, '[', ei.curch} case stateParams: ret := []rune{0x1b, '['} for _, s := range ei.csiParam { ret = append(ret, []rune(s)...) ret = append(ret, ';') } return append(ret, ei.curch) } return nil } // newEscapeInterpreter returns an escapeInterpreter that will be able to parse // terminal escape sequences. func newEscapeInterpreter(mode OutputMode) *escapeInterpreter { ei := &escapeInterpreter{ state: stateNone, curFgColor: ColorDefault, curBgColor: ColorDefault, mode: mode, } return ei } // reset sets the escapeInterpreter in initial state. func (ei *escapeInterpreter) reset() { ei.state = stateNone ei.curFgColor = ColorDefault ei.curBgColor = ColorDefault ei.csiParam = nil } // parseOne parses a rune. If isEscape is true, it means that the rune is part // of an escape sequence, and as such should not be printed verbatim. Otherwise, // it's not an escape sequence. func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { // Sanity checks if len(ei.csiParam) > 20 { return false, errCSITooLong } if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 { return false, errCSITooLong } ei.curch = ch switch ei.state { case stateNone: if ch == 0x1b { ei.state = stateEscape return true, nil } return false, nil case stateEscape: if ch == '[' { ei.state = stateCSI return true, nil } return false, errNotCSI case stateCSI: switch { case ch >= '0' && ch <= '9': ei.csiParam = append(ei.csiParam, "") case ch == 'm': ei.csiParam = append(ei.csiParam, "0") default: return false, errCSIParseError } ei.state = stateParams fallthrough case stateParams: switch { case ch >= '0' && ch <= '9': ei.csiParam[len(ei.csiParam)-1] += string(ch) return true, nil case ch == ';': ei.csiParam = append(ei.csiParam, "") return true, nil case ch == 'm': var err error switch ei.mode { case OutputNormal: err = ei.outputNormal() case Output256: err = ei.output256() } if err != nil { return false, errCSIParseError } ei.state = stateNone ei.csiParam = nil return true, nil default: return false, errCSIParseError } } return false, nil } // outputNormal provides 8 different colors: // black, red, green, yellow, blue, magenta, cyan, white func (ei *escapeInterpreter) outputNormal() error { for _, param := range ei.csiParam { p, err := strconv.Atoi(param) if err != nil { return errCSIParseError } switch { case p >= 30 && p <= 37: ei.curFgColor = Attribute(p - 30 + 1) case p == 39: ei.curFgColor = ColorDefault case p >= 40 && p <= 47: ei.curBgColor = Attribute(p - 40 + 1) case p == 49: ei.curBgColor = ColorDefault case p == 1: ei.curFgColor |= AttrBold case p == 4: ei.curFgColor |= AttrUnderline case p == 7: ei.curFgColor |= AttrReverse case p == 0: ei.curFgColor = ColorDefault ei.curBgColor = ColorDefault } } return nil } // output256 allows you to leverage the 256-colors terminal mode: // 0x01 - 0x08: the 8 colors as in OutputNormal // 0x09 - 0x10: Color* | AttrBold // 0x11 - 0xe8: 216 different colors // 0xe9 - 0x1ff: 24 different shades of grey func (ei *escapeInterpreter) output256() error { if len(ei.csiParam) < 3 { return ei.outputNormal() } mode, err := strconv.Atoi(ei.csiParam[1]) if err != nil { return errCSIParseError } if mode != 5 { return ei.outputNormal() } fgbg, err := strconv.Atoi(ei.csiParam[0]) if err != nil { return errCSIParseError } color, err := strconv.Atoi(ei.csiParam[2]) if err != nil { return errCSIParseError } switch fgbg { case 38: ei.curFgColor = Attribute(color + 1) for _, param := range ei.csiParam[3:] { p, err := strconv.Atoi(param) if err != nil { return errCSIParseError } switch { case p == 1: ei.curFgColor |= AttrBold case p == 4: ei.curFgColor |= AttrUnderline case p == 7: ei.curFgColor |= AttrReverse } } case 48: ei.curBgColor = Attribute(color + 1) default: return errCSIParseError } return nil } gocui-0.4.0/gui.go000066400000000000000000000375471326343017700137620ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import ( "errors" "github.com/nsf/termbox-go" ) var ( // ErrQuit is used to decide if the MainLoop finished successfully. ErrQuit = errors.New("quit") // ErrUnknownView allows to assert if a View must be initialized. ErrUnknownView = errors.New("unknown view") ) // OutputMode represents the terminal's output mode (8 or 256 colors). type OutputMode termbox.OutputMode const ( // OutputNormal provides 8-colors terminal mode. OutputNormal = OutputMode(termbox.OutputNormal) // Output256 provides 256-colors terminal mode. Output256 = OutputMode(termbox.Output256) ) // Gui represents the whole User Interface, including the views, layouts // and keybindings. type Gui struct { tbEvents chan termbox.Event userEvents chan userEvent views []*View currentView *View managers []Manager keybindings []*keybinding maxX, maxY int outputMode OutputMode // BgColor and FgColor allow to configure the background and foreground // colors of the GUI. BgColor, FgColor Attribute // SelBgColor and SelFgColor allow to configure the background and // foreground colors of the frame of the current view. SelBgColor, SelFgColor Attribute // If Highlight is true, Sel{Bg,Fg}Colors will be used to draw the // frame of the current view. Highlight bool // If Cursor is true then the cursor is enabled. Cursor bool // If Mouse is true then mouse events will be enabled. Mouse bool // If InputEsc is true, when ESC sequence is in the buffer and it doesn't // match any known sequence, ESC means KeyEsc. InputEsc bool // If ASCII is true then use ASCII instead of unicode to draw the // interface. Using ASCII is more portable. ASCII bool } // NewGui returns a new Gui object with a given output mode. func NewGui(mode OutputMode) (*Gui, error) { if err := termbox.Init(); err != nil { return nil, err } g := &Gui{} g.outputMode = mode termbox.SetOutputMode(termbox.OutputMode(mode)) g.tbEvents = make(chan termbox.Event, 20) g.userEvents = make(chan userEvent, 20) g.maxX, g.maxY = termbox.Size() g.BgColor, g.FgColor = ColorDefault, ColorDefault g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault return g, nil } // Close finalizes the library. It should be called after a successful // initialization and when gocui is not needed anymore. func (g *Gui) Close() { termbox.Close() } // Size returns the terminal's size. func (g *Gui) Size() (x, y int) { return g.maxX, g.maxY } // SetRune writes a rune at the given point, relative to the top-left // corner of the terminal. It checks if the position is valid and applies // the given colors. func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error { if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY { return errors.New("invalid point") } termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor)) return nil } // Rune returns the rune contained in the cell at the given position. // It checks if the position is valid. func (g *Gui) Rune(x, y int) (rune, error) { if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY { return ' ', errors.New("invalid point") } c := termbox.CellBuffer()[y*g.maxX+x] return c.Ch, nil } // SetView creates a new view with its top-left corner at (x0, y0) // and the bottom-right one at (x1, y1). If a view with the same name // already exists, its dimensions are updated; otherwise, the error // ErrUnknownView is returned, which allows to assert if the View must // be initialized. It checks if the position is valid. func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) { if x0 >= x1 || y0 >= y1 { return nil, errors.New("invalid dimensions") } if name == "" { return nil, errors.New("invalid name") } if v, err := g.View(name); err == nil { v.x0 = x0 v.y0 = y0 v.x1 = x1 v.y1 = y1 v.tainted = true return v, nil } v := newView(name, x0, y0, x1, y1, g.outputMode) v.BgColor, v.FgColor = g.BgColor, g.FgColor v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor g.views = append(g.views, v) return v, ErrUnknownView } // SetViewOnTop sets the given view on top of the existing ones. func (g *Gui) SetViewOnTop(name string) (*View, error) { for i, v := range g.views { if v.name == name { s := append(g.views[:i], g.views[i+1:]...) g.views = append(s, v) return v, nil } } return nil, ErrUnknownView } // SetViewOnBottom sets the given view on bottom of the existing ones. func (g *Gui) SetViewOnBottom(name string) (*View, error) { for i, v := range g.views { if v.name == name { s := append(g.views[:i], g.views[i+1:]...) g.views = append([]*View{v}, s...) return v, nil } } return nil, ErrUnknownView } // Views returns all the views in the GUI. func (g *Gui) Views() []*View { return g.views } // View returns a pointer to the view with the given name, or error // ErrUnknownView if a view with that name does not exist. func (g *Gui) View(name string) (*View, error) { for _, v := range g.views { if v.name == name { return v, nil } } return nil, ErrUnknownView } // ViewByPosition returns a pointer to a view matching the given position, or // error ErrUnknownView if a view in that position does not exist. func (g *Gui) ViewByPosition(x, y int) (*View, error) { // traverse views in reverse order checking top views first for i := len(g.views); i > 0; i-- { v := g.views[i-1] if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 { return v, nil } } return nil, ErrUnknownView } // ViewPosition returns the coordinates of the view with the given name, or // error ErrUnknownView if a view with that name does not exist. func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) { for _, v := range g.views { if v.name == name { return v.x0, v.y0, v.x1, v.y1, nil } } return 0, 0, 0, 0, ErrUnknownView } // DeleteView deletes a view by name. func (g *Gui) DeleteView(name string) error { for i, v := range g.views { if v.name == name { g.views = append(g.views[:i], g.views[i+1:]...) return nil } } return ErrUnknownView } // SetCurrentView gives the focus to a given view. func (g *Gui) SetCurrentView(name string) (*View, error) { for _, v := range g.views { if v.name == name { g.currentView = v return v, nil } } return nil, ErrUnknownView } // CurrentView returns the currently focused view, or nil if no view // owns the focus. func (g *Gui) CurrentView() *View { return g.currentView } // SetKeybinding creates a new keybinding. If viewname equals to "" // (empty string) then the keybinding will apply to all views. key must // be a rune or a Key. func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, handler func(*Gui, *View) error) error { var kb *keybinding k, ch, err := getKey(key) if err != nil { return err } kb = newKeybinding(viewname, k, ch, mod, handler) g.keybindings = append(g.keybindings, kb) return nil } // DeleteKeybinding deletes a keybinding. func (g *Gui) DeleteKeybinding(viewname string, key interface{}, mod Modifier) error { k, ch, err := getKey(key) if err != nil { return err } for i, kb := range g.keybindings { if kb.viewName == viewname && kb.ch == ch && kb.key == k && kb.mod == mod { g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...) return nil } } return errors.New("keybinding not found") } // DeleteKeybindings deletes all keybindings of view. func (g *Gui) DeleteKeybindings(viewname string) { var s []*keybinding for _, kb := range g.keybindings { if kb.viewName != viewname { s = append(s, kb) } } g.keybindings = s } // getKey takes an empty interface with a key and returns the corresponding // typed Key or rune. func getKey(key interface{}) (Key, rune, error) { switch t := key.(type) { case Key: return t, 0, nil case rune: return 0, t, nil default: return 0, 0, errors.New("unknown type") } } // userEvent represents an event triggered by the user. type userEvent struct { f func(*Gui) error } // Update executes the passed function. This method can be called safely from a // goroutine in order to update the GUI. It is important to note that the // passed function won't be executed immediately, instead it will be added to // the user events queue. Given that Update spawns a goroutine, the order in // which the user events will be handled is not guaranteed. func (g *Gui) Update(f func(*Gui) error) { go func() { g.userEvents <- userEvent{f: f} }() } // A Manager is in charge of GUI's layout and can be used to build widgets. type Manager interface { // Layout is called every time the GUI is redrawn, it must contain the // base views and its initializations. Layout(*Gui) error } // The ManagerFunc type is an adapter to allow the use of ordinary functions as // Managers. If f is a function with the appropriate signature, ManagerFunc(f) // is an Manager object that calls f. type ManagerFunc func(*Gui) error // Layout calls f(g) func (f ManagerFunc) Layout(g *Gui) error { return f(g) } // SetManager sets the given GUI managers. It deletes all views and // keybindings. func (g *Gui) SetManager(managers ...Manager) { g.managers = managers g.currentView = nil g.views = nil g.keybindings = nil go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }() } // SetManagerFunc sets the given manager function. It deletes all views and // keybindings. func (g *Gui) SetManagerFunc(manager func(*Gui) error) { g.SetManager(ManagerFunc(manager)) } // MainLoop runs the main loop until an error is returned. A successful // finish should return ErrQuit. func (g *Gui) MainLoop() error { go func() { for { g.tbEvents <- termbox.PollEvent() } }() inputMode := termbox.InputAlt if g.InputEsc { inputMode = termbox.InputEsc } if g.Mouse { inputMode |= termbox.InputMouse } termbox.SetInputMode(inputMode) if err := g.flush(); err != nil { return err } for { select { case ev := <-g.tbEvents: if err := g.handleEvent(&ev); err != nil { return err } case ev := <-g.userEvents: if err := ev.f(g); err != nil { return err } } if err := g.consumeevents(); err != nil { return err } if err := g.flush(); err != nil { return err } } } // consumeevents handles the remaining events in the events pool. func (g *Gui) consumeevents() error { for { select { case ev := <-g.tbEvents: if err := g.handleEvent(&ev); err != nil { return err } case ev := <-g.userEvents: if err := ev.f(g); err != nil { return err } default: return nil } } } // handleEvent handles an event, based on its type (key-press, error, // etc.) func (g *Gui) handleEvent(ev *termbox.Event) error { switch ev.Type { case termbox.EventKey, termbox.EventMouse: return g.onKey(ev) case termbox.EventError: return ev.Err default: return nil } } // flush updates the gui, re-drawing frames and buffers. func (g *Gui) flush() error { termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor)) maxX, maxY := termbox.Size() // if GUI's size has changed, we need to redraw all views if maxX != g.maxX || maxY != g.maxY { for _, v := range g.views { v.tainted = true } } g.maxX, g.maxY = maxX, maxY for _, m := range g.managers { if err := m.Layout(g); err != nil { return err } } for _, v := range g.views { if v.Frame { var fgColor, bgColor Attribute if g.Highlight && v == g.currentView { fgColor = g.SelFgColor bgColor = g.SelBgColor } else { fgColor = g.FgColor bgColor = g.BgColor } if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil { return err } if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil { return err } if v.Title != "" { if err := g.drawTitle(v, fgColor, bgColor); err != nil { return err } } } if err := g.draw(v); err != nil { return err } } termbox.Flush() return nil } // drawFrameEdges draws the horizontal and vertical edges of a view. func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { runeH, runeV := '─', '│' if g.ASCII { runeH, runeV = '-', '|' } for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ { if x < 0 { continue } if v.y0 > -1 && v.y0 < g.maxY { if err := g.SetRune(x, v.y0, runeH, fgColor, bgColor); err != nil { return err } } if v.y1 > -1 && v.y1 < g.maxY { if err := g.SetRune(x, v.y1, runeH, fgColor, bgColor); err != nil { return err } } } for y := v.y0 + 1; y < v.y1 && y < g.maxY; y++ { if y < 0 { continue } if v.x0 > -1 && v.x0 < g.maxX { if err := g.SetRune(v.x0, y, runeV, fgColor, bgColor); err != nil { return err } } if v.x1 > -1 && v.x1 < g.maxX { if err := g.SetRune(v.x1, y, runeV, fgColor, bgColor); err != nil { return err } } } return nil } // drawFrameCorners draws the corners of the view. func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error { runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘' if g.ASCII { runeTL, runeTR, runeBL, runeBR = '+', '+', '+', '+' } corners := []struct { x, y int ch rune }{{v.x0, v.y0, runeTL}, {v.x1, v.y0, runeTR}, {v.x0, v.y1, runeBL}, {v.x1, v.y1, runeBR}} for _, c := range corners { if c.x >= 0 && c.y >= 0 && c.x < g.maxX && c.y < g.maxY { if err := g.SetRune(c.x, c.y, c.ch, fgColor, bgColor); err != nil { return err } } } return nil } // drawTitle draws the title of the view. func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error { if v.y0 < 0 || v.y0 >= g.maxY { return nil } for i, ch := range v.Title { x := v.x0 + i + 2 if x < 0 { continue } else if x > v.x1-2 || x >= g.maxX { break } if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil { return err } } return nil } // draw manages the cursor and calls the draw function of a view. func (g *Gui) draw(v *View) error { if g.Cursor { if curview := g.currentView; curview != nil { vMaxX, vMaxY := curview.Size() if curview.cx < 0 { curview.cx = 0 } else if curview.cx >= vMaxX { curview.cx = vMaxX - 1 } if curview.cy < 0 { curview.cy = 0 } else if curview.cy >= vMaxY { curview.cy = vMaxY - 1 } gMaxX, gMaxY := g.Size() cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1 if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY { termbox.SetCursor(cx, cy) } else { termbox.HideCursor() } } } else { termbox.HideCursor() } v.clearRunes() if err := v.draw(); err != nil { return err } return nil } // onKey manages key-press events. A keybinding handler is called when // a key-press or mouse event satisfies a configured keybinding. Furthermore, // currentView's internal buffer is modified if currentView.Editable is true. func (g *Gui) onKey(ev *termbox.Event) error { switch ev.Type { case termbox.EventKey: matched, err := g.execKeybindings(g.currentView, ev) if err != nil { return err } if matched { break } if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil { g.currentView.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod)) } case termbox.EventMouse: mx, my := ev.MouseX, ev.MouseY v, err := g.ViewByPosition(mx, my) if err != nil { break } if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil { return err } if _, err := g.execKeybindings(v, ev); err != nil { return err } } return nil } // execKeybindings executes the keybinding handlers that match the passed view // and event. The value of matched is true if there is a match and no errors. func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) { matched = false for _, kb := range g.keybindings { if kb.handler == nil { continue } if kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) && kb.matchView(v) { if err := kb.handler(g, v); err != nil { return false, err } matched = true } } return matched, nil } gocui-0.4.0/keybinding.go000066400000000000000000000114701326343017700153040ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import "github.com/nsf/termbox-go" // Keybidings are used to link a given key-press event with a handler. type keybinding struct { viewName string key Key ch rune mod Modifier handler func(*Gui, *View) error } // newKeybinding returns a new Keybinding object. func newKeybinding(viewname string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) { kb = &keybinding{ viewName: viewname, key: key, ch: ch, mod: mod, handler: handler, } return kb } // matchKeypress returns if the keybinding matches the keypress. func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool { return kb.key == key && kb.ch == ch && kb.mod == mod } // matchView returns if the keybinding matches the current view. func (kb *keybinding) matchView(v *View) bool { if kb.viewName == "" { return true } return v != nil && kb.viewName == v.name } // Key represents special keys or keys combinations. type Key termbox.Key // Special keys. const ( KeyF1 Key = Key(termbox.KeyF1) KeyF2 = Key(termbox.KeyF2) KeyF3 = Key(termbox.KeyF3) KeyF4 = Key(termbox.KeyF4) KeyF5 = Key(termbox.KeyF5) KeyF6 = Key(termbox.KeyF6) KeyF7 = Key(termbox.KeyF7) KeyF8 = Key(termbox.KeyF8) KeyF9 = Key(termbox.KeyF9) KeyF10 = Key(termbox.KeyF10) KeyF11 = Key(termbox.KeyF11) KeyF12 = Key(termbox.KeyF12) KeyInsert = Key(termbox.KeyInsert) KeyDelete = Key(termbox.KeyDelete) KeyHome = Key(termbox.KeyHome) KeyEnd = Key(termbox.KeyEnd) KeyPgup = Key(termbox.KeyPgup) KeyPgdn = Key(termbox.KeyPgdn) KeyArrowUp = Key(termbox.KeyArrowUp) KeyArrowDown = Key(termbox.KeyArrowDown) KeyArrowLeft = Key(termbox.KeyArrowLeft) KeyArrowRight = Key(termbox.KeyArrowRight) MouseLeft = Key(termbox.MouseLeft) MouseMiddle = Key(termbox.MouseMiddle) MouseRight = Key(termbox.MouseRight) MouseRelease = Key(termbox.MouseRelease) MouseWheelUp = Key(termbox.MouseWheelUp) MouseWheelDown = Key(termbox.MouseWheelDown) ) // Keys combinations. const ( KeyCtrlTilde Key = Key(termbox.KeyCtrlTilde) KeyCtrl2 = Key(termbox.KeyCtrl2) KeyCtrlSpace = Key(termbox.KeyCtrlSpace) KeyCtrlA = Key(termbox.KeyCtrlA) KeyCtrlB = Key(termbox.KeyCtrlB) KeyCtrlC = Key(termbox.KeyCtrlC) KeyCtrlD = Key(termbox.KeyCtrlD) KeyCtrlE = Key(termbox.KeyCtrlE) KeyCtrlF = Key(termbox.KeyCtrlF) KeyCtrlG = Key(termbox.KeyCtrlG) KeyBackspace = Key(termbox.KeyBackspace) KeyCtrlH = Key(termbox.KeyCtrlH) KeyTab = Key(termbox.KeyTab) KeyCtrlI = Key(termbox.KeyCtrlI) KeyCtrlJ = Key(termbox.KeyCtrlJ) KeyCtrlK = Key(termbox.KeyCtrlK) KeyCtrlL = Key(termbox.KeyCtrlL) KeyEnter = Key(termbox.KeyEnter) KeyCtrlM = Key(termbox.KeyCtrlM) KeyCtrlN = Key(termbox.KeyCtrlN) KeyCtrlO = Key(termbox.KeyCtrlO) KeyCtrlP = Key(termbox.KeyCtrlP) KeyCtrlQ = Key(termbox.KeyCtrlQ) KeyCtrlR = Key(termbox.KeyCtrlR) KeyCtrlS = Key(termbox.KeyCtrlS) KeyCtrlT = Key(termbox.KeyCtrlT) KeyCtrlU = Key(termbox.KeyCtrlU) KeyCtrlV = Key(termbox.KeyCtrlV) KeyCtrlW = Key(termbox.KeyCtrlW) KeyCtrlX = Key(termbox.KeyCtrlX) KeyCtrlY = Key(termbox.KeyCtrlY) KeyCtrlZ = Key(termbox.KeyCtrlZ) KeyEsc = Key(termbox.KeyEsc) KeyCtrlLsqBracket = Key(termbox.KeyCtrlLsqBracket) KeyCtrl3 = Key(termbox.KeyCtrl3) KeyCtrl4 = Key(termbox.KeyCtrl4) KeyCtrlBackslash = Key(termbox.KeyCtrlBackslash) KeyCtrl5 = Key(termbox.KeyCtrl5) KeyCtrlRsqBracket = Key(termbox.KeyCtrlRsqBracket) KeyCtrl6 = Key(termbox.KeyCtrl6) KeyCtrl7 = Key(termbox.KeyCtrl7) KeyCtrlSlash = Key(termbox.KeyCtrlSlash) KeyCtrlUnderscore = Key(termbox.KeyCtrlUnderscore) KeySpace = Key(termbox.KeySpace) KeyBackspace2 = Key(termbox.KeyBackspace2) KeyCtrl8 = Key(termbox.KeyCtrl8) ) // Modifier allows to define special keys combinations. They can be used // in combination with Keys or Runes when a new keybinding is defined. type Modifier termbox.Modifier // Modifiers. const ( ModNone Modifier = Modifier(0) ModAlt = Modifier(termbox.ModAlt) ) gocui-0.4.0/view.go000066400000000000000000000267071326343017700141440ustar00rootroot00000000000000// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import ( "bytes" "errors" "io" "strings" "github.com/nsf/termbox-go" ) // A View is a window. It maintains its own internal buffer and cursor // position. type View struct { name string x0, y0, x1, y1 int ox, oy int cx, cy int lines [][]cell readOffset int readCache string tainted bool // marks if the viewBuffer must be updated viewLines []viewLine // internal representation of the view's buffer ei *escapeInterpreter // used to decode ESC sequences on Write // BgColor and FgColor allow to configure the background and foreground // colors of the View. BgColor, FgColor Attribute // SelBgColor and SelFgColor are used to configure the background and // foreground colors of the selected line, when it is highlighted. SelBgColor, SelFgColor Attribute // If Editable is true, keystrokes will be added to the view's internal // buffer at the cursor position. Editable bool // Editor allows to define the editor that manages the edition mode, // including keybindings or cursor behaviour. DefaultEditor is used by // default. Editor Editor // Overwrite enables or disables the overwrite mode of the view. Overwrite bool // If Highlight is true, Sel{Bg,Fg}Colors will be used // for the line under the cursor position. Highlight bool // If Frame is true, a border will be drawn around the view. Frame bool // If Wrap is true, the content that is written to this View is // automatically wrapped when it is longer than its width. If true the // view's x-origin will be ignored. Wrap bool // If Autoscroll is true, the View will automatically scroll down when the // text overflows. If true the view's y-origin will be ignored. Autoscroll bool // If Frame is true, Title allows to configure a title for the view. Title string // If Mask is true, the View will display the mask instead of the real // content Mask rune } type viewLine struct { linesX, linesY int // coordinates relative to v.lines line []cell } type cell struct { chr rune bgColor, fgColor Attribute } type lineType []cell // String returns a string from a given cell slice. func (l lineType) String() string { str := "" for _, c := range l { str += string(c.chr) } return str } // newView returns a new View object. func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View { v := &View{ name: name, x0: x0, y0: y0, x1: x1, y1: y1, Frame: true, Editor: DefaultEditor, tainted: true, ei: newEscapeInterpreter(mode), } return v } // Size returns the number of visible columns and rows in the View. func (v *View) Size() (x, y int) { return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1 } // Name returns the name of the view. func (v *View) Name() string { return v.name } // setRune sets a rune at the given point relative to the view. It applies the // specified colors, taking into account if the cell must be highlighted. Also, // it checks if the position is valid. func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error { maxX, maxY := v.Size() if x < 0 || x >= maxX || y < 0 || y >= maxY { return errors.New("invalid point") } var ( ry, rcy int err error ) if v.Highlight { _, ry, err = v.realPosition(x, y) if err != nil { return err } _, rcy, err = v.realPosition(v.cx, v.cy) if err != nil { return err } } if v.Mask != 0 { fgColor = v.FgColor bgColor = v.BgColor ch = v.Mask } else if v.Highlight && ry == rcy { fgColor = v.SelFgColor bgColor = v.SelBgColor } termbox.SetCell(v.x0+x+1, v.y0+y+1, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor)) return nil } // SetCursor sets the cursor position of the view at the given point, // relative to the view. It checks if the position is valid. func (v *View) SetCursor(x, y int) error { maxX, maxY := v.Size() if x < 0 || x >= maxX || y < 0 || y >= maxY { return errors.New("invalid point") } v.cx = x v.cy = y return nil } // Cursor returns the cursor position of the view. func (v *View) Cursor() (x, y int) { return v.cx, v.cy } // SetOrigin sets the origin position of the view's internal buffer, // so the buffer starts to be printed from this point, which means that // it is linked with the origin point of view. It can be used to // implement Horizontal and Vertical scrolling with just incrementing // or decrementing ox and oy. func (v *View) SetOrigin(x, y int) error { if x < 0 || y < 0 { return errors.New("invalid point") } v.ox = x v.oy = y return nil } // Origin returns the origin position of the view. func (v *View) Origin() (x, y int) { return v.ox, v.oy } // Write appends a byte slice into the view's internal buffer. Because // View implements the io.Writer interface, it can be passed as parameter // of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must // be called to clear the view's buffer. func (v *View) Write(p []byte) (n int, err error) { v.tainted = true for _, ch := range bytes.Runes(p) { switch ch { case '\n': v.lines = append(v.lines, nil) case '\r': nl := len(v.lines) if nl > 0 { v.lines[nl-1] = nil } else { v.lines = make([][]cell, 1) } default: cells := v.parseInput(ch) if cells == nil { continue } nl := len(v.lines) if nl > 0 { v.lines[nl-1] = append(v.lines[nl-1], cells...) } else { v.lines = append(v.lines, cells) } } } return len(p), nil } // parseInput parses char by char the input written to the View. It returns nil // while processing ESC sequences. Otherwise, it returns a cell slice that // contains the processed data. func (v *View) parseInput(ch rune) []cell { cells := []cell{} isEscape, err := v.ei.parseOne(ch) if err != nil { for _, r := range v.ei.runes() { c := cell{ fgColor: v.FgColor, bgColor: v.BgColor, chr: r, } cells = append(cells, c) } v.ei.reset() } else { if isEscape { return nil } c := cell{ fgColor: v.ei.curFgColor, bgColor: v.ei.curBgColor, chr: ch, } cells = append(cells, c) } return cells } // Read reads data into p. It returns the number of bytes read into p. // At EOF, err will be io.EOF. Calling Read() after Rewind() makes the // cache to be refreshed with the contents of the view. func (v *View) Read(p []byte) (n int, err error) { if v.readOffset == 0 { v.readCache = v.Buffer() } if v.readOffset < len(v.readCache) { n = copy(p, v.readCache[v.readOffset:]) v.readOffset += n } else { err = io.EOF } return } // Rewind sets the offset for the next Read to 0, which also refresh the // read cache. func (v *View) Rewind() { v.readOffset = 0 } // draw re-draws the view's contents. func (v *View) draw() error { maxX, maxY := v.Size() if v.Wrap { if maxX == 0 { return errors.New("X size of the view cannot be 0") } v.ox = 0 } if v.tainted { v.viewLines = nil for i, line := range v.lines { if v.Wrap { if len(line) < maxX { vline := viewLine{linesX: 0, linesY: i, line: line} v.viewLines = append(v.viewLines, vline) continue } else { for n := 0; n <= len(line); n += maxX { if len(line[n:]) <= maxX { vline := viewLine{linesX: n, linesY: i, line: line[n:]} v.viewLines = append(v.viewLines, vline) } else { vline := viewLine{linesX: n, linesY: i, line: line[n : n+maxX]} v.viewLines = append(v.viewLines, vline) } } } } else { vline := viewLine{linesX: 0, linesY: i, line: line} v.viewLines = append(v.viewLines, vline) } } v.tainted = false } if v.Autoscroll && len(v.viewLines) > maxY { v.oy = len(v.viewLines) - maxY } y := 0 for i, vline := range v.viewLines { if i < v.oy { continue } if y >= maxY { break } x := 0 for j, c := range vline.line { if j < v.ox { continue } if x >= maxX { break } fgColor := c.fgColor if fgColor == ColorDefault { fgColor = v.FgColor } bgColor := c.bgColor if bgColor == ColorDefault { bgColor = v.BgColor } if err := v.setRune(x, y, c.chr, fgColor, bgColor); err != nil { return err } x++ } y++ } return nil } // realPosition returns the position in the internal buffer corresponding to the // point (x, y) of the view. func (v *View) realPosition(vx, vy int) (x, y int, err error) { vx = v.ox + vx vy = v.oy + vy if vx < 0 || vy < 0 { return 0, 0, errors.New("invalid point") } if len(v.viewLines) == 0 { return vx, vy, nil } if vy < len(v.viewLines) { vline := v.viewLines[vy] x = vline.linesX + vx y = vline.linesY } else { vline := v.viewLines[len(v.viewLines)-1] x = vx y = vline.linesY + vy - len(v.viewLines) + 1 } return x, y, nil } // Clear empties the view's internal buffer. func (v *View) Clear() { v.tainted = true v.lines = nil v.viewLines = nil v.readOffset = 0 v.clearRunes() } // clearRunes erases all the cells in the view. func (v *View) clearRunes() { maxX, maxY := v.Size() for x := 0; x < maxX; x++ { for y := 0; y < maxY; y++ { termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ', termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor)) } } } // BufferLines returns the lines in the view's internal // buffer. func (v *View) BufferLines() []string { lines := make([]string, len(v.lines)) for i, l := range v.lines { str := lineType(l).String() str = strings.Replace(str, "\x00", " ", -1) lines[i] = str } return lines } // Buffer returns a string with the contents of the view's internal // buffer. func (v *View) Buffer() string { str := "" for _, l := range v.lines { str += lineType(l).String() + "\n" } return strings.Replace(str, "\x00", " ", -1) } // ViewBufferLines returns the lines in the view's internal // buffer that is shown to the user. func (v *View) ViewBufferLines() []string { lines := make([]string, len(v.viewLines)) for i, l := range v.viewLines { str := lineType(l.line).String() str = strings.Replace(str, "\x00", " ", -1) lines[i] = str } return lines } // ViewBuffer returns a string with the contents of the view's buffer that is // shown to the user. func (v *View) ViewBuffer() string { str := "" for _, l := range v.viewLines { str += lineType(l.line).String() + "\n" } return strings.Replace(str, "\x00", " ", -1) } // Line returns a string with the line of the view's internal buffer // at the position corresponding to the point (x, y). func (v *View) Line(y int) (string, error) { _, y, err := v.realPosition(0, y) if err != nil { return "", err } if y < 0 || y >= len(v.lines) { return "", errors.New("invalid point") } return lineType(v.lines[y]).String(), nil } // Word returns a string with the word of the view's internal buffer // at the position corresponding to the point (x, y). func (v *View) Word(x, y int) (string, error) { x, y, err := v.realPosition(x, y) if err != nil { return "", err } if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) { return "", errors.New("invalid point") } str := lineType(v.lines[y]).String() nl := strings.LastIndexFunc(str[:x], indexFunc) if nl == -1 { nl = 0 } else { nl = nl + 1 } nr := strings.IndexFunc(str[x:], indexFunc) if nr == -1 { nr = len(str) } else { nr = nr + x } return string(str[nl:nr]), nil } // indexFunc allows to split lines by words taking into account spaces // and 0. func indexFunc(r rune) bool { return r == ' ' || r == 0 }