The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── _examples
    ├── Mark.Twain-Tom.Sawyer.txt
    ├── active.go
    ├── bufs.go
    ├── colors.go
    ├── colors256.go
    ├── demo.go
    ├── dynamic.go
    ├── flow_layout.go
    ├── goroutine.go
    ├── hello.go
    ├── layout.go
    ├── mask.go
    ├── mouse.go
    ├── ontop.go
    ├── overlap.go
    ├── size.go
    ├── stdin.go
    ├── title.go
    ├── widgets.go
    └── wrap.go
├── attribute.go
├── doc.go
├── edit.go
├── escape.go
├── go.mod
├── go.sum
├── gui.go
├── keybinding.go
└── view.go


/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | 


--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
 1 | # This is the official list of gocui authors for copyright purposes.
 2 | 
 3 | # Names should be added to this file as
 4 | #	Name or Organization <email address> contribution
 5 | #		Contribution
 6 | # The email address is not required for organizations.
 7 | 
 8 | Roi Martin <jroi.martin@gmail.com>
 9 | 	Main developer
10 | 
11 | Ryan Sullivan <kayoticsully@gmail.com>
12 | 	Toggleable view frames
13 | 
14 | Matthieu Rakotojaona <matthieu.rakotojaona@gmail.com>
15 | 	Wrapped views
16 | 
17 | Harry Lawrence <hazbo@gmx.com>
18 | 	Basic mouse support
19 | 
20 | Danny Tylman <dtylman@gmail.com>
21 | 	Masked views
22 | 
23 | Frederik Deweerdt <frederik.deweerdt@gmail.com>
24 | 	Colored fonts
25 | 
26 | Henri Koski <henri.t.koski@gmail.com>
27 | 	Custom current view color
28 | 
29 | Dustin Willis Webber <dustin.webber@gmail.com>
30 | 	256-colors output mode support
31 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2014 The gocui Authors. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are met:
 5 |     * Redistributions of source code must retain the above copyright
 6 |       notice, this list of conditions and the following disclaimer.
 7 |     * Redistributions in binary form must reproduce the above copyright
 8 |       notice, this list of conditions and the following disclaimer in the
 9 |       documentation and/or other materials provided with the distribution.
10 |     * Neither the name of the gocui Authors nor the names of its contributors
11 |       may be used to endorse or promote products derived from this software
12 |       without specific prior written permission.
13 | 
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # GOCUI - Go Console User Interface
 2 | 
 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/jroimartin/gocui.svg)](https://pkg.go.dev/github.com/jroimartin/gocui)
 4 | 
 5 | Minimalist Go package aimed at creating Console User Interfaces.
 6 | 
 7 | ## Features
 8 | 
 9 | * Minimalist API.
10 | * Views (the "windows" in the GUI) implement the interface io.ReadWriter.
11 | * Support for overlapping views.
12 | * The GUI can be modified at runtime (concurrent-safe).
13 | * Global and view-level keybindings.
14 | * Mouse support.
15 | * Colored text.
16 | * Customizable edition mode.
17 | * Easy to build reusable widgets, complex layouts...
18 | 
19 | ## Installation
20 | 
21 | Execute:
22 | 
23 | ```sh
24 | go get github.com/jroimartin/gocui
25 | ```
26 | 
27 | ## Documentation
28 | 
29 | Execute:
30 | 
31 | ```sh
32 | go doc github.com/jroimartin/gocui
33 | ```
34 | 
35 | Or visit [pkg.go.dev](https://pkg.go.dev/github.com/jroimartin/gocui) to read
36 | it online.
37 | 
38 | ## Example
39 | 
40 | ```go
41 | package main
42 | 
43 | import (
44 | 	"fmt"
45 | 	"log"
46 | 
47 | 	"github.com/jroimartin/gocui"
48 | )
49 | 
50 | func main() {
51 | 	g, err := gocui.NewGui(gocui.OutputNormal)
52 | 	if err != nil {
53 | 		log.Panicln(err)
54 | 	}
55 | 	defer g.Close()
56 | 
57 | 	g.SetManagerFunc(layout)
58 | 
59 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
60 | 		log.Panicln(err)
61 | 	}
62 | 
63 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
64 | 		log.Panicln(err)
65 | 	}
66 | }
67 | 
68 | func layout(g *gocui.Gui) error {
69 | 	maxX, maxY := g.Size()
70 | 	if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
71 | 		if err != gocui.ErrUnknownView {
72 | 			return err
73 | 		}
74 | 		fmt.Fprintln(v, "Hello world!")
75 | 	}
76 | 	return nil
77 | }
78 | 
79 | func quit(g *gocui.Gui, v *gocui.View) error {
80 | 	return gocui.ErrQuit
81 | }
82 | ```
83 | 
84 | ## Screenshots
85 | 
86 | ![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png)
87 | 
88 | ![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png)
89 | 
90 | ![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png)
91 | 


--------------------------------------------------------------------------------
/_examples/Mark.Twain-Tom.Sawyer.txt:
--------------------------------------------------------------------------------
  1 | The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
  2 | by Mark Twain (Samuel Clemens)
  3 | 
  4 | This eBook is for the use of anyone anywhere at no cost and with
  5 | almost no restrictions whatsoever.  You may copy it, give it away or
  6 | re-use it under the terms of the Project Gutenberg License included
  7 | with this eBook or online at www.gutenberg.net
  8 | 
  9 | 
 10 | Title: The Adventures of Tom Sawyer, Complete
 11 | 
 12 | Author: Mark Twain (Samuel Clemens)
 13 | 
 14 | Release Date: August 20, 2006 [EBook #74]
 15 | [Last updated: May 3, 2011]
 16 | 
 17 | Language: English
 18 | 
 19 | 
 20 | *** START OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER ***
 21 | 
 22 | 
 23 | 
 24 | 
 25 | Produced by David Widger. The previous edition was updated by Jose
 26 | Menendez.
 27 | 
 28 | 
 29 | 
 30 | 
 31 | 
 32 |                    THE ADVENTURES OF TOM SAWYER
 33 |                                 BY
 34 |                             MARK TWAIN
 35 |                      (Samuel Langhorne Clemens)
 36 | 
 37 | 
 38 | 
 39 | 
 40 |                            P R E F A C E
 41 | 
 42 | MOST of the adventures recorded in this book really occurred; one or
 43 | two were experiences of my own, the rest those of boys who were
 44 | schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but
 45 | not from an individual--he is a combination of the characteristics of
 46 | three boys whom I knew, and therefore belongs to the composite order of
 47 | architecture.
 48 | 
 49 | The odd superstitions touched upon were all prevalent among children
 50 | and slaves in the West at the period of this story--that is to say,
 51 | thirty or forty years ago.
 52 | 
 53 | Although my book is intended mainly for the entertainment of boys and
 54 | girls, I hope it will not be shunned by men and women on that account,
 55 | for part of my plan has been to try to pleasantly remind adults of what
 56 | they once were themselves, and of how they felt and thought and talked,
 57 | and what queer enterprises they sometimes engaged in.
 58 | 
 59 |                                                             THE AUTHOR.
 60 | 
 61 | HARTFORD, 1876.
 62 | 
 63 | 
 64 | 
 65 |                           T O M   S A W Y E R
 66 | 
 67 | 
 68 | 
 69 | CHAPTER I
 70 | 
 71 | "TOM!"
 72 | 
 73 | No answer.
 74 | 
 75 | "TOM!"
 76 | 
 77 | No answer.
 78 | 
 79 | "What's gone with that boy,  I wonder? You TOM!"
 80 | 
 81 | No answer.
 82 | 
 83 | The old lady pulled her spectacles down and looked over them about the
 84 | room; then she put them up and looked out under them. She seldom or
 85 | never looked THROUGH them for so small a thing as a boy; they were her
 86 | state pair, the pride of her heart, and were built for "style," not
 87 | service--she could have seen through a pair of stove-lids just as well.
 88 | She looked perplexed for a moment, and then said, not fiercely, but
 89 | still loud enough for the furniture to hear:
 90 | 
 91 | "Well, I lay if I get hold of you I'll--"
 92 | 
 93 | She did not finish, for by this time she was bending down and punching
 94 | under the bed with the broom, and so she needed breath to punctuate the
 95 | punches with. She resurrected nothing but the cat.
 96 | 
 97 | "I never did see the beat of that boy!"
 98 | 
 99 | She went to the open door and stood in it and looked out among the
100 | tomato vines and "jimpson" weeds that constituted the garden. No Tom.
101 | So she lifted up her voice at an angle calculated for distance and
102 | shouted:
103 | 
104 | "Y-o-u-u TOM!"
105 | 
106 | There was a slight noise behind her and she turned just in time to
107 | seize a small boy by the slack of his roundabout and arrest his flight.
108 | 
109 | "There! I might 'a' thought of that closet. What you been doing in
110 | there?"
111 | 
112 | "Nothing."
113 | 
114 | "Nothing! Look at your hands. And look at your mouth. What IS that
115 | truck?"
116 | 
117 | "I don't know, aunt."
118 | 
119 | "Well, I know. It's jam--that's what it is. Forty times I've said if
120 | you didn't let that jam alone I'd skin you. Hand me that switch."
121 | 
122 | The switch hovered in the air--the peril was desperate--
123 | 
124 | "My! Look behind you, aunt!"
125 | 
126 | The old lady whirled round, and snatched her skirts out of danger. The
127 | lad fled on the instant, scrambled up the high board-fence, and
128 | disappeared over it.
129 | 
130 | His aunt Polly stood surprised a moment, and then broke into a gentle
131 | laugh.
132 | 
133 | "Hang the boy, can't I never learn anything? Ain't he played me tricks
134 | enough like that for me to be looking out for him by this time? But old
135 | fools is the biggest fools there is. Can't learn an old dog new tricks,
136 | as the saying is. But my goodness, he never plays them alike, two days,
137 | and how is a body to know what's coming? He 'pears to know just how
138 | long he can torment me before I get my dander up, and he knows if he
139 | can make out to put me off for a minute or make me laugh, it's all down
140 | again and I can't hit him a lick. I ain't doing my duty by that boy,
141 | and that's the Lord's truth, goodness knows. Spare the rod and spile
142 | the child, as the Good Book says. I'm a laying up sin and suffering for
143 | us both, I know. He's full of the Old Scratch, but laws-a-me! he's my
144 | own dead sister's boy, poor thing, and I ain't got the heart to lash
145 | him, somehow. Every time I let him off, my conscience does hurt me so,
146 | and every time I hit him my old heart most breaks. Well-a-well, man
147 | that is born of woman is of few days and full of trouble, as the
148 | Scripture says, and I reckon it's so. He'll play hookey this evening, *
149 | and [* Southwestern for "afternoon"] I'll just be obleeged to make him
150 | work, to-morrow, to punish him. It's mighty hard to make him work
151 | Saturdays, when all the boys is having holiday, but he hates work more
152 | than he hates anything else, and I've GOT to do some of my duty by him,
153 | or I'll be the ruination of the child."
154 | 
155 | Tom did play hookey, and he had a very good time. He got back home
156 | barely in season to help Jim, the small colored boy, saw next-day's
157 | wood and split the kindlings before supper--at least he was there in
158 | time to tell his adventures to Jim while Jim did three-fourths of the
159 | work. Tom's younger brother (or rather half-brother) Sid was already
160 | through with his part of the work (picking up chips), for he was a
161 | quiet boy, and had no adventurous, troublesome ways.
162 | 
163 | While Tom was eating his supper, and stealing sugar as opportunity
164 | offered, Aunt Polly asked him questions that were full of guile, and
165 | very deep--for she wanted to trap him into damaging revealments. Like
166 | many other simple-hearted souls, it was her pet vanity to believe she
167 | was endowed with a talent for dark and mysterious diplomacy, and she
168 | loved to contemplate her most transparent devices as marvels of low
169 | cunning. Said she:
170 | 
171 | "Tom, it was middling warm in school, warn't it?"
172 | 
173 | "Yes'm."
174 | 
175 | "Powerful warm, warn't it?"
176 | 
177 | "Yes'm."
178 | 
179 | "Didn't you want to go in a-swimming, Tom?"
180 | 
181 | A bit of a scare shot through Tom--a touch of uncomfortable suspicion.
182 | He searched Aunt Polly's face, but it told him nothing. So he said:
183 | 
184 | "No'm--well, not very much."
185 | 
186 | The old lady reached out her hand and felt Tom's shirt, and said:
187 | 
188 | "But you ain't too warm now, though." And it flattered her to reflect
189 | that she had discovered that the shirt was dry without anybody knowing
190 | that that was what she had in her mind. But in spite of her, Tom knew
191 | where the wind lay, now. So he forestalled what might be the next move:
192 | 
193 | "Some of us pumped on our heads--mine's damp yet. See?"
194 | 
195 | Aunt Polly was vexed to think she had overlooked that bit of
196 | circumstantial evidence, and missed a trick. Then she had a new
197 | inspiration:
198 | 
199 | "Tom, you didn't have to undo your shirt collar where I sewed it, to
200 | pump on your head, did you? Unbutton your jacket!"
201 | 
202 | The trouble vanished out of Tom's face. He opened his jacket. His
203 | shirt collar was securely sewed.
204 | 
205 | "Bother! Well, go 'long with you. I'd made sure you'd played hookey
206 | and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a
207 | singed cat, as the saying is--better'n you look. THIS time."
208 | 
209 | She was half sorry her sagacity had miscarried, and half glad that Tom
210 | had stumbled into obedient conduct for once.
211 | 
212 | But Sidney said:
213 | 
214 | "Well, now, if I didn't think you sewed his collar with white thread,
215 | but it's black."
216 | 
217 | "Why, I did sew it with white! Tom!"
218 | 
219 | But Tom did not wait for the rest. As he went out at the door he said:
220 | 
221 | "Siddy, I'll lick you for that."
222 | 
223 | In a safe place Tom examined two large needles which were thrust into
224 | the lapels of his jacket, and had thread bound about them--one needle
225 | carried white thread and the other black. He said:
226 | 
227 | "She'd never noticed if it hadn't been for Sid. Confound it! sometimes
228 | she sews it with white, and sometimes she sews it with black. I wish to
229 | geeminy she'd stick to one or t'other--I can't keep the run of 'em. But
230 | I bet you I'll lam Sid for that. I'll learn him!"
231 | 
232 | He was not the Model Boy of the village. He knew the model boy very
233 | well though--and loathed him.
234 | 
235 | Within two minutes, or even less, he had forgotten all his troubles.
236 | Not because his troubles were one whit less heavy and bitter to him
237 | than a man's are to a man, but because a new and powerful interest bore
238 | them down and drove them out of his mind for the time--just as men's
239 | misfortunes are forgotten in the excitement of new enterprises. This
240 | new interest was a valued novelty in whistling, which he had just
241 | acquired from a negro, and he was suffering to practise it undisturbed.
242 | It consisted in a peculiar bird-like turn, a sort of liquid warble,
243 | produced by touching the tongue to the roof of the mouth at short
244 | intervals in the midst of the music--the reader probably remembers how
245 | to do it, if he has ever been a boy. Diligence and attention soon gave
246 | him the knack of it, and he strode down the street with his mouth full
247 | of harmony and his soul full of gratitude. He felt much as an
248 | astronomer feels who has discovered a new planet--no doubt, as far as
249 | strong, deep, unalloyed pleasure is concerned, the advantage was with
250 | the boy, not the astronomer.
251 | 
252 | The summer evenings were long. It was not dark, yet. Presently Tom
253 | checked his whistle. A stranger was before him--a boy a shade larger
254 | than himself. A new-comer of any age or either sex was an impressive
255 | curiosity in the poor little shabby village of St. Petersburg. This boy
256 | was well dressed, too--well dressed on a week-day. This was simply
257 | astounding. His cap was a dainty thing, his close-buttoned blue cloth
258 | roundabout was new and natty, and so were his pantaloons. He had shoes
259 | on--and it was only Friday. He even wore a necktie, a bright bit of
260 | ribbon. He had a citified air about him that ate into Tom's vitals. The
261 | more Tom stared at the splendid marvel, the higher he turned up his
262 | nose at his finery and the shabbier and shabbier his own outfit seemed
263 | to him to grow. Neither boy spoke. If one moved, the other moved--but
264 | only sidewise, in a circle; they kept face to face and eye to eye all
265 | the time. Finally Tom said:
266 | 
267 | "I can lick you!"
268 | 
269 | "I'd like to see you try it."
270 | 
271 | "Well, I can do it."
272 | 
273 | "No you can't, either."
274 | 
275 | "Yes I can."
276 | 
277 | "No you can't."
278 | 
279 | "I can."
280 | 
281 | "You can't."
282 | 
283 | "Can!"
284 | 
285 | "Can't!"
286 | 
287 | An uncomfortable pause. Then Tom said:
288 | 
289 | "What's your name?"
290 | 
291 | "'Tisn't any of your business, maybe."
292 | 
293 | "Well I 'low I'll MAKE it my business."
294 | 
295 | "Well why don't you?"
296 | 
297 | "If you say much, I will."
298 | 
299 | "Much--much--MUCH. There now."
300 | 
301 | "Oh, you think you're mighty smart, DON'T you? I could lick you with
302 | one hand tied behind me, if I wanted to."
303 | 
304 | "Well why don't you DO it? You SAY you can do it."
305 | 
306 | "Well I WILL, if you fool with me."
307 | 
308 | "Oh yes--I've seen whole families in the same fix."
309 | 
310 | "Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!"
311 | 
312 | "You can lump that hat if you don't like it. I dare you to knock it
313 | off--and anybody that'll take a dare will suck eggs."
314 | 
315 | "You're a liar!"
316 | 
317 | "You're another."
318 | 
319 | "You're a fighting liar and dasn't take it up."
320 | 
321 | "Aw--take a walk!"
322 | 
323 | "Say--if you give me much more of your sass I'll take and bounce a
324 | rock off'n your head."
325 | 
326 | "Oh, of COURSE you will."
327 | 
328 | "Well I WILL."
329 | 
330 | "Well why don't you DO it then? What do you keep SAYING you will for?
331 | Why don't you DO it? It's because you're afraid."
332 | 
333 | "I AIN'T afraid."
334 | 
335 | "You are."
336 | 
337 | "I ain't."
338 | 
339 | "You are."
340 | 
341 | Another pause, and more eying and sidling around each other. Presently
342 | they were shoulder to shoulder. Tom said:
343 | 
344 | "Get away from here!"
345 | 
346 | "Go away yourself!"
347 | 
348 | "I won't."
349 | 
350 | "I won't either."
351 | 
352 | So they stood, each with a foot placed at an angle as a brace, and
353 | both shoving with might and main, and glowering at each other with
354 | hate. But neither could get an advantage. After struggling till both
355 | were hot and flushed, each relaxed his strain with watchful caution,
356 | and Tom said:
357 | 
358 | "You're a coward and a pup. I'll tell my big brother on you, and he
359 | can thrash you with his little finger, and I'll make him do it, too."
360 | 
361 | "What do I care for your big brother? I've got a brother that's bigger
362 | than he is--and what's more, he can throw him over that fence, too."
363 | [Both brothers were imaginary.]
364 | 
365 | "That's a lie."
366 | 
367 | "YOUR saying so don't make it so."
368 | 
369 | Tom drew a line in the dust with his big toe, and said:
370 | 
371 | "I dare you to step over that, and I'll lick you till you can't stand
372 | up. Anybody that'll take a dare will steal sheep."
373 | 
374 | The new boy stepped over promptly, and said:
375 | 
376 | "Now you said you'd do it, now let's see you do it."
377 | 
378 | "Don't you crowd me now; you better look out."
379 | 
380 | "Well, you SAID you'd do it--why don't you do it?"
381 | 
382 | "By jingo! for two cents I WILL do it."
383 | 
384 | The new boy took two broad coppers out of his pocket and held them out
385 | with derision. Tom struck them to the ground. In an instant both boys
386 | were rolling and tumbling in the dirt, gripped together like cats; and
387 | for the space of a minute they tugged and tore at each other's hair and
388 | clothes, punched and scratched each other's nose, and covered
389 | themselves with dust and glory. Presently the confusion took form, and
390 | through the fog of battle Tom appeared, seated astride the new boy, and
391 | pounding him with his fists. "Holler 'nuff!" said he.
392 | 
393 | The boy only struggled to free himself. He was crying--mainly from rage.
394 | 
395 | "Holler 'nuff!"--and the pounding went on.
396 | 
397 | At last the stranger got out a smothered "'Nuff!" and Tom let him up
398 | and said:
399 | 
400 | "Now that'll learn you. Better look out who you're fooling with next
401 | time."
402 | 
403 | The new boy went off brushing the dust from his clothes, sobbing,
404 | snuffling, and occasionally looking back and shaking his head and
405 | threatening what he would do to Tom the "next time he caught him out."
406 | To which Tom responded with jeers, and started off in high feather, and
407 | as soon as his back was turned the new boy snatched up a stone, threw
408 | it and hit him between the shoulders and then turned tail and ran like
409 | an antelope. Tom chased the traitor home, and thus found out where he
410 | lived. He then held a position at the gate for some time, daring the
411 | enemy to come outside, but the enemy only made faces at him through the
412 | window and declined. At last the enemy's mother appeared, and called
413 | Tom a bad, vicious, vulgar child, and ordered him away. So he went
414 | away; but he said he "'lowed" to "lay" for that boy.
415 | 
416 | He got home pretty late that night, and when he climbed cautiously in
417 | at the window, he uncovered an ambuscade, in the person of his aunt;
418 | and when she saw the state his clothes were in her resolution to turn
419 | his Saturday holiday into captivity at hard labor became adamantine in
420 | its firmness.
421 | 


--------------------------------------------------------------------------------
/_examples/active.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"fmt"
  9 | 	"log"
 10 | 
 11 | 	"github.com/jroimartin/gocui"
 12 | )
 13 | 
 14 | var (
 15 | 	viewArr = []string{"v1", "v2", "v3", "v4"}
 16 | 	active  = 0
 17 | )
 18 | 
 19 | func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) {
 20 | 	if _, err := g.SetCurrentView(name); err != nil {
 21 | 		return nil, err
 22 | 	}
 23 | 	return g.SetViewOnTop(name)
 24 | }
 25 | 
 26 | func nextView(g *gocui.Gui, v *gocui.View) error {
 27 | 	nextIndex := (active + 1) % len(viewArr)
 28 | 	name := viewArr[nextIndex]
 29 | 
 30 | 	out, err := g.View("v2")
 31 | 	if err != nil {
 32 | 		return err
 33 | 	}
 34 | 	fmt.Fprintln(out, "Going from view "+v.Name()+" to "+name)
 35 | 
 36 | 	if _, err := setCurrentViewOnTop(g, name); err != nil {
 37 | 		return err
 38 | 	}
 39 | 
 40 | 	if nextIndex == 0 || nextIndex == 3 {
 41 | 		g.Cursor = true
 42 | 	} else {
 43 | 		g.Cursor = false
 44 | 	}
 45 | 
 46 | 	active = nextIndex
 47 | 	return nil
 48 | }
 49 | 
 50 | func layout(g *gocui.Gui) error {
 51 | 	maxX, maxY := g.Size()
 52 | 	if v, err := g.SetView("v1", 0, 0, maxX/2-1, maxY/2-1); err != nil {
 53 | 		if err != gocui.ErrUnknownView {
 54 | 			return err
 55 | 		}
 56 | 		v.Title = "v1 (editable)"
 57 | 		v.Editable = true
 58 | 		v.Wrap = true
 59 | 
 60 | 		if _, err = setCurrentViewOnTop(g, "v1"); err != nil {
 61 | 			return err
 62 | 		}
 63 | 	}
 64 | 
 65 | 	if v, err := g.SetView("v2", maxX/2-1, 0, maxX-1, maxY/2-1); err != nil {
 66 | 		if err != gocui.ErrUnknownView {
 67 | 			return err
 68 | 		}
 69 | 		v.Title = "v2"
 70 | 		v.Wrap = true
 71 | 		v.Autoscroll = true
 72 | 	}
 73 | 	if v, err := g.SetView("v3", 0, maxY/2-1, maxX/2-1, maxY-1); err != nil {
 74 | 		if err != gocui.ErrUnknownView {
 75 | 			return err
 76 | 		}
 77 | 		v.Title = "v3"
 78 | 		v.Wrap = true
 79 | 		v.Autoscroll = true
 80 | 		fmt.Fprint(v, "Press TAB to change current view")
 81 | 	}
 82 | 	if v, err := g.SetView("v4", maxX/2, maxY/2, maxX-1, maxY-1); err != nil {
 83 | 		if err != gocui.ErrUnknownView {
 84 | 			return err
 85 | 		}
 86 | 		v.Title = "v4 (editable)"
 87 | 		v.Editable = true
 88 | 	}
 89 | 	return nil
 90 | }
 91 | 
 92 | func quit(g *gocui.Gui, v *gocui.View) error {
 93 | 	return gocui.ErrQuit
 94 | }
 95 | 
 96 | func main() {
 97 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 98 | 	if err != nil {
 99 | 		log.Panicln(err)
100 | 	}
101 | 	defer g.Close()
102 | 
103 | 	g.Highlight = true
104 | 	g.Cursor = true
105 | 	g.SelFgColor = gocui.ColorGreen
106 | 
107 | 	g.SetManagerFunc(layout)
108 | 
109 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
110 | 		log.Panicln(err)
111 | 	}
112 | 	if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
113 | 		log.Panicln(err)
114 | 	}
115 | 
116 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
117 | 		log.Panicln(err)
118 | 	}
119 | }
120 | 


--------------------------------------------------------------------------------
/_examples/bufs.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | // WARNING: tricky code just for testing purposes, do not use as reference.
 6 | 
 7 | package main
 8 | 
 9 | import (
10 | 	"fmt"
11 | 	"log"
12 | 
13 | 	"github.com/jroimartin/gocui"
14 | )
15 | 
16 | var vbuf, buf string
17 | 
18 | func quit(g *gocui.Gui, v *gocui.View) error {
19 | 	vbuf = v.ViewBuffer()
20 | 	buf = v.Buffer()
21 | 	return gocui.ErrQuit
22 | }
23 | 
24 | func overwrite(g *gocui.Gui, v *gocui.View) error {
25 | 	v.Overwrite = !v.Overwrite
26 | 	return nil
27 | }
28 | 
29 | func layout(g *gocui.Gui) error {
30 | 	_, maxY := g.Size()
31 | 	if v, err := g.SetView("main", 0, 0, 20, maxY-1); err != nil {
32 | 		if err != gocui.ErrUnknownView {
33 | 			return err
34 | 		}
35 | 		v.Editable = true
36 | 		v.Wrap = true
37 | 		if _, err := g.SetCurrentView("main"); err != nil {
38 | 			return err
39 | 		}
40 | 	}
41 | 	return nil
42 | }
43 | 
44 | func main() {
45 | 	g, err := gocui.NewGui(gocui.OutputNormal)
46 | 	if err != nil {
47 | 		log.Panicln(err)
48 | 	}
49 | 
50 | 	g.Cursor = true
51 | 	g.Mouse = true
52 | 
53 | 	g.SetManagerFunc(layout)
54 | 
55 | 	if err := g.SetKeybinding("main", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
56 | 		log.Panicln(err)
57 | 	}
58 | 	if err := g.SetKeybinding("main", gocui.KeyCtrlI, gocui.ModNone, overwrite); err != nil {
59 | 		log.Panicln(err)
60 | 	}
61 | 
62 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
63 | 		log.Panicln(err)
64 | 	}
65 | 
66 | 	g.Close()
67 | 
68 | 	fmt.Printf("VBUF:\n%s\n", vbuf)
69 | 	fmt.Printf("BUF:\n%s\n", buf)
70 | }
71 | 


--------------------------------------------------------------------------------
/_examples/colors.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
16 | 	if err != nil {
17 | 		log.Panicln(err)
18 | 	}
19 | 	defer g.Close()
20 | 
21 | 	g.SetManagerFunc(layout)
22 | 
23 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
24 | 		log.Panicln(err)
25 | 	}
26 | 
27 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
28 | 		log.Panicln(err)
29 | 	}
30 | }
31 | 
32 | func layout(g *gocui.Gui) error {
33 | 	maxX, maxY := g.Size()
34 | 	if v, err := g.SetView("colors", maxX/2-7, maxY/2-12, maxX/2+7, maxY/2+13); err != nil {
35 | 		if err != gocui.ErrUnknownView {
36 | 			return err
37 | 		}
38 | 		for i := 0; i <= 7; i++ {
39 | 			for _, j := range []int{1, 4, 7} {
40 | 				fmt.Fprintf(v, "Hello \033[3%d;%dmcolors!\033[0m\n", i, j)
41 | 			}
42 | 		}
43 | 	}
44 | 	return nil
45 | }
46 | 
47 | func quit(g *gocui.Gui, v *gocui.View) error {
48 | 	return gocui.ErrQuit
49 | }
50 | 


--------------------------------------------------------------------------------
/_examples/colors256.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.Output256)
16 | 
17 | 	if err != nil {
18 | 		log.Panicln(err)
19 | 	}
20 | 	defer g.Close()
21 | 
22 | 	g.SetManagerFunc(layout)
23 | 
24 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
25 | 		log.Panicln(err)
26 | 	}
27 | 
28 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
29 | 		log.Panicln(err)
30 | 	}
31 | }
32 | 
33 | func layout(g *gocui.Gui) error {
34 | 	maxX, maxY := g.Size()
35 | 	if v, err := g.SetView("colors", -1, -1, maxX, maxY); err != nil {
36 | 		if err != gocui.ErrUnknownView {
37 | 			return err
38 | 		}
39 | 
40 | 		// 256-colors escape codes
41 | 		for i := 0; i < 256; i++ {
42 | 			str := fmt.Sprintf("\x1b[48;5;%dm\x1b[30m%3d\x1b[0m ", i, i)
43 | 			str += fmt.Sprintf("\x1b[38;5;%dm%3d\x1b[0m ", i, i)
44 | 
45 | 			if (i+1)%10 == 0 {
46 | 				str += "\n"
47 | 			}
48 | 
49 | 			fmt.Fprint(v, str)
50 | 		}
51 | 
52 | 		fmt.Fprint(v, "\n\n")
53 | 
54 | 		// 8-colors escape codes
55 | 		ctr := 0
56 | 		for i := 0; i <= 7; i++ {
57 | 			for _, j := range []int{1, 4, 7} {
58 | 				str := fmt.Sprintf("\x1b[3%d;%dm%d:%d\x1b[0m ", i, j, i, j)
59 | 				if (ctr+1)%20 == 0 {
60 | 					str += "\n"
61 | 				}
62 | 
63 | 				fmt.Fprint(v, str)
64 | 
65 | 				ctr++
66 | 			}
67 | 		}
68 | 	}
69 | 	return nil
70 | }
71 | 
72 | func quit(g *gocui.Gui, v *gocui.View) error {
73 | 	return gocui.ErrQuit
74 | }
75 | 


--------------------------------------------------------------------------------
/_examples/demo.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"fmt"
  9 | 	"io"
 10 | 	"io/ioutil"
 11 | 	"log"
 12 | 	"strings"
 13 | 
 14 | 	"github.com/jroimartin/gocui"
 15 | )
 16 | 
 17 | func nextView(g *gocui.Gui, v *gocui.View) error {
 18 | 	if v == nil || v.Name() == "side" {
 19 | 		_, err := g.SetCurrentView("main")
 20 | 		return err
 21 | 	}
 22 | 	_, err := g.SetCurrentView("side")
 23 | 	return err
 24 | }
 25 | 
 26 | func cursorDown(g *gocui.Gui, v *gocui.View) error {
 27 | 	if v != nil {
 28 | 		cx, cy := v.Cursor()
 29 | 		if err := v.SetCursor(cx, cy+1); err != nil {
 30 | 			ox, oy := v.Origin()
 31 | 			if err := v.SetOrigin(ox, oy+1); err != nil {
 32 | 				return err
 33 | 			}
 34 | 		}
 35 | 	}
 36 | 	return nil
 37 | }
 38 | 
 39 | func cursorUp(g *gocui.Gui, v *gocui.View) error {
 40 | 	if v != nil {
 41 | 		ox, oy := v.Origin()
 42 | 		cx, cy := v.Cursor()
 43 | 		if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
 44 | 			if err := v.SetOrigin(ox, oy-1); err != nil {
 45 | 				return err
 46 | 			}
 47 | 		}
 48 | 	}
 49 | 	return nil
 50 | }
 51 | 
 52 | func getLine(g *gocui.Gui, v *gocui.View) error {
 53 | 	var l string
 54 | 	var err error
 55 | 
 56 | 	_, cy := v.Cursor()
 57 | 	if l, err = v.Line(cy); err != nil {
 58 | 		l = ""
 59 | 	}
 60 | 
 61 | 	maxX, maxY := g.Size()
 62 | 	if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
 63 | 		if err != gocui.ErrUnknownView {
 64 | 			return err
 65 | 		}
 66 | 		fmt.Fprintln(v, l)
 67 | 		if _, err := g.SetCurrentView("msg"); err != nil {
 68 | 			return err
 69 | 		}
 70 | 	}
 71 | 	return nil
 72 | }
 73 | 
 74 | func delMsg(g *gocui.Gui, v *gocui.View) error {
 75 | 	if err := g.DeleteView("msg"); err != nil {
 76 | 		return err
 77 | 	}
 78 | 	if _, err := g.SetCurrentView("side"); err != nil {
 79 | 		return err
 80 | 	}
 81 | 	return nil
 82 | }
 83 | 
 84 | func quit(g *gocui.Gui, v *gocui.View) error {
 85 | 	return gocui.ErrQuit
 86 | }
 87 | 
 88 | func keybindings(g *gocui.Gui) error {
 89 | 	if err := g.SetKeybinding("side", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
 90 | 		return err
 91 | 	}
 92 | 	if err := g.SetKeybinding("main", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
 93 | 		return err
 94 | 	}
 95 | 	if err := g.SetKeybinding("side", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
 96 | 		return err
 97 | 	}
 98 | 	if err := g.SetKeybinding("side", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
 99 | 		return err
100 | 	}
101 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
102 | 		return err
103 | 	}
104 | 	if err := g.SetKeybinding("side", gocui.KeyEnter, gocui.ModNone, getLine); err != nil {
105 | 		return err
106 | 	}
107 | 	if err := g.SetKeybinding("msg", gocui.KeyEnter, gocui.ModNone, delMsg); err != nil {
108 | 		return err
109 | 	}
110 | 
111 | 	if err := g.SetKeybinding("main", gocui.KeyCtrlS, gocui.ModNone, saveMain); err != nil {
112 | 		return err
113 | 	}
114 | 	if err := g.SetKeybinding("main", gocui.KeyCtrlW, gocui.ModNone, saveVisualMain); err != nil {
115 | 		return err
116 | 	}
117 | 	return nil
118 | }
119 | 
120 | func saveMain(g *gocui.Gui, v *gocui.View) error {
121 | 	f, err := ioutil.TempFile("", "gocui_demo_")
122 | 	if err != nil {
123 | 		return err
124 | 	}
125 | 	defer f.Close()
126 | 
127 | 	p := make([]byte, 5)
128 | 	v.Rewind()
129 | 	for {
130 | 		n, err := v.Read(p)
131 | 		if n > 0 {
132 | 			if _, err := f.Write(p[:n]); err != nil {
133 | 				return err
134 | 			}
135 | 		}
136 | 		if err == io.EOF {
137 | 			break
138 | 		}
139 | 		if err != nil {
140 | 			return err
141 | 		}
142 | 	}
143 | 	return nil
144 | }
145 | 
146 | func saveVisualMain(g *gocui.Gui, v *gocui.View) error {
147 | 	f, err := ioutil.TempFile("", "gocui_demo_")
148 | 	if err != nil {
149 | 		return err
150 | 	}
151 | 	defer f.Close()
152 | 
153 | 	vb := v.ViewBuffer()
154 | 	if _, err := io.Copy(f, strings.NewReader(vb)); err != nil {
155 | 		return err
156 | 	}
157 | 	return nil
158 | }
159 | 
160 | func layout(g *gocui.Gui) error {
161 | 	maxX, maxY := g.Size()
162 | 	if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil {
163 | 		if err != gocui.ErrUnknownView {
164 | 			return err
165 | 		}
166 | 		v.Highlight = true
167 | 		v.SelBgColor = gocui.ColorGreen
168 | 		v.SelFgColor = gocui.ColorBlack
169 | 		fmt.Fprintln(v, "Item 1")
170 | 		fmt.Fprintln(v, "Item 2")
171 | 		fmt.Fprintln(v, "Item 3")
172 | 		fmt.Fprint(v, "\rWill be")
173 | 		fmt.Fprint(v, "deleted\rItem 4\nItem 5")
174 | 	}
175 | 	if v, err := g.SetView("main", 30, -1, maxX, maxY); err != nil {
176 | 		if err != gocui.ErrUnknownView {
177 | 			return err
178 | 		}
179 | 		b, err := ioutil.ReadFile("Mark.Twain-Tom.Sawyer.txt")
180 | 		if err != nil {
181 | 			panic(err)
182 | 		}
183 | 		fmt.Fprintf(v, "%s", b)
184 | 		v.Editable = true
185 | 		v.Wrap = true
186 | 		if _, err := g.SetCurrentView("main"); err != nil {
187 | 			return err
188 | 		}
189 | 	}
190 | 	return nil
191 | }
192 | 
193 | func main() {
194 | 	g, err := gocui.NewGui(gocui.OutputNormal)
195 | 	if err != nil {
196 | 		log.Panicln(err)
197 | 	}
198 | 	defer g.Close()
199 | 
200 | 	g.Cursor = true
201 | 
202 | 	g.SetManagerFunc(layout)
203 | 
204 | 	if err := keybindings(g); err != nil {
205 | 		log.Panicln(err)
206 | 	}
207 | 
208 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
209 | 		log.Panicln(err)
210 | 	}
211 | }
212 | 


--------------------------------------------------------------------------------
/_examples/dynamic.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"fmt"
  9 | 	"log"
 10 | 	"strings"
 11 | 
 12 | 	"github.com/jroimartin/gocui"
 13 | )
 14 | 
 15 | const delta = 1
 16 | 
 17 | var (
 18 | 	views   = []string{}
 19 | 	curView = -1
 20 | 	idxView = 0
 21 | )
 22 | 
 23 | func main() {
 24 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 25 | 	if err != nil {
 26 | 		log.Panicln(err)
 27 | 	}
 28 | 	defer g.Close()
 29 | 
 30 | 	g.Highlight = true
 31 | 	g.SelFgColor = gocui.ColorRed
 32 | 
 33 | 	g.SetManagerFunc(layout)
 34 | 
 35 | 	if err := initKeybindings(g); err != nil {
 36 | 		log.Panicln(err)
 37 | 	}
 38 | 	if err := newView(g); err != nil {
 39 | 		log.Panicln(err)
 40 | 	}
 41 | 
 42 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
 43 | 		log.Panicln(err)
 44 | 	}
 45 | }
 46 | 
 47 | func layout(g *gocui.Gui) error {
 48 | 	maxX, _ := g.Size()
 49 | 	v, err := g.SetView("help", maxX-25, 0, maxX-1, 9)
 50 | 	if err != nil {
 51 | 		if err != gocui.ErrUnknownView {
 52 | 			return err
 53 | 		}
 54 | 		fmt.Fprintln(v, "KEYBINDINGS")
 55 | 		fmt.Fprintln(v, "Space: New View")
 56 | 		fmt.Fprintln(v, "Tab: Next View")
 57 | 		fmt.Fprintln(v, "← ↑ → ↓: Move View")
 58 | 		fmt.Fprintln(v, "Backspace: Delete View")
 59 | 		fmt.Fprintln(v, "t: Set view on top")
 60 | 		fmt.Fprintln(v, "b: Set view on bottom")
 61 | 		fmt.Fprintln(v, "^C: Exit")
 62 | 	}
 63 | 	return nil
 64 | }
 65 | 
 66 | func initKeybindings(g *gocui.Gui) error {
 67 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
 68 | 		func(g *gocui.Gui, v *gocui.View) error {
 69 | 			return gocui.ErrQuit
 70 | 		}); err != nil {
 71 | 		return err
 72 | 	}
 73 | 	if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone,
 74 | 		func(g *gocui.Gui, v *gocui.View) error {
 75 | 			return newView(g)
 76 | 		}); err != nil {
 77 | 		return err
 78 | 	}
 79 | 	if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone,
 80 | 		func(g *gocui.Gui, v *gocui.View) error {
 81 | 			return delView(g)
 82 | 		}); err != nil {
 83 | 		return err
 84 | 	}
 85 | 	if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone,
 86 | 		func(g *gocui.Gui, v *gocui.View) error {
 87 | 			return nextView(g, true)
 88 | 		}); err != nil {
 89 | 		return err
 90 | 	}
 91 | 	if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone,
 92 | 		func(g *gocui.Gui, v *gocui.View) error {
 93 | 			return moveView(g, v, -delta, 0)
 94 | 		}); err != nil {
 95 | 		return err
 96 | 	}
 97 | 	if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone,
 98 | 		func(g *gocui.Gui, v *gocui.View) error {
 99 | 			return moveView(g, v, delta, 0)
100 | 		}); err != nil {
101 | 		return err
102 | 	}
103 | 	if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone,
104 | 		func(g *gocui.Gui, v *gocui.View) error {
105 | 			return moveView(g, v, 0, delta)
106 | 		}); err != nil {
107 | 		return err
108 | 	}
109 | 	if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone,
110 | 		func(g *gocui.Gui, v *gocui.View) error {
111 | 			return moveView(g, v, 0, -delta)
112 | 		}); err != nil {
113 | 		return err
114 | 	}
115 | 	if err := g.SetKeybinding("", 't', gocui.ModNone,
116 | 		func(g *gocui.Gui, v *gocui.View) error {
117 | 			_, err := g.SetViewOnTop(views[curView])
118 | 			return err
119 | 		}); err != nil {
120 | 		return err
121 | 	}
122 | 	if err := g.SetKeybinding("", 'b', gocui.ModNone,
123 | 		func(g *gocui.Gui, v *gocui.View) error {
124 | 			_, err := g.SetViewOnBottom(views[curView])
125 | 			return err
126 | 		}); err != nil {
127 | 		return err
128 | 	}
129 | 	return nil
130 | }
131 | 
132 | func newView(g *gocui.Gui) error {
133 | 	maxX, maxY := g.Size()
134 | 	name := fmt.Sprintf("v%v", idxView)
135 | 	v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5)
136 | 	if err != nil {
137 | 		if err != gocui.ErrUnknownView {
138 | 			return err
139 | 		}
140 | 		v.Wrap = true
141 | 		fmt.Fprintln(v, strings.Repeat(name+" ", 30))
142 | 	}
143 | 	if _, err := g.SetCurrentView(name); err != nil {
144 | 		return err
145 | 	}
146 | 
147 | 	views = append(views, name)
148 | 	curView = len(views) - 1
149 | 	idxView += 1
150 | 	return nil
151 | }
152 | 
153 | func delView(g *gocui.Gui) error {
154 | 	if len(views) <= 1 {
155 | 		return nil
156 | 	}
157 | 
158 | 	if err := g.DeleteView(views[curView]); err != nil {
159 | 		return err
160 | 	}
161 | 	views = append(views[:curView], views[curView+1:]...)
162 | 
163 | 	return nextView(g, false)
164 | }
165 | 
166 | func nextView(g *gocui.Gui, disableCurrent bool) error {
167 | 	next := curView + 1
168 | 	if next > len(views)-1 {
169 | 		next = 0
170 | 	}
171 | 
172 | 	if _, err := g.SetCurrentView(views[next]); err != nil {
173 | 		return err
174 | 	}
175 | 
176 | 	curView = next
177 | 	return nil
178 | }
179 | 
180 | func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error {
181 | 	name := v.Name()
182 | 	x0, y0, x1, y1, err := g.ViewPosition(name)
183 | 	if err != nil {
184 | 		return err
185 | 	}
186 | 	if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy); err != nil {
187 | 		return err
188 | 	}
189 | 	return nil
190 | }
191 | 


--------------------------------------------------------------------------------
/_examples/flow_layout.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 	"strings"
11 | 
12 | 	"github.com/jroimartin/gocui"
13 | )
14 | 
15 | type Label struct {
16 | 	name string
17 | 	w, h int
18 | 	body string
19 | }
20 | 
21 | func NewLabel(name string, body string) *Label {
22 | 	lines := strings.Split(body, "\n")
23 | 
24 | 	w := 0
25 | 	for _, l := range lines {
26 | 		if len(l) > w {
27 | 			w = len(l)
28 | 		}
29 | 	}
30 | 	h := len(lines) + 1
31 | 	w = w + 1
32 | 
33 | 	return &Label{name: name, w: w, h: h, body: body}
34 | }
35 | 
36 | func (w *Label) Layout(g *gocui.Gui) error {
37 | 	v, err := g.SetView(w.name, 0, 0, w.w, w.h)
38 | 	if err != nil {
39 | 		if err != gocui.ErrUnknownView {
40 | 			return err
41 | 		}
42 | 		fmt.Fprint(v, w.body)
43 | 	}
44 | 	return nil
45 | }
46 | 
47 | func flowLayout(g *gocui.Gui) error {
48 | 	views := g.Views()
49 | 	x := 0
50 | 	for _, v := range views {
51 | 		w, h := v.Size()
52 | 		_, err := g.SetView(v.Name(), x, 0, x+w+1, h+1)
53 | 		if err != nil && err != gocui.ErrUnknownView {
54 | 			return err
55 | 		}
56 | 		x += w + 2
57 | 	}
58 | 	return nil
59 | }
60 | 
61 | func main() {
62 | 	g, err := gocui.NewGui(gocui.OutputNormal)
63 | 	if err != nil {
64 | 		log.Panicln(err)
65 | 	}
66 | 	defer g.Close()
67 | 
68 | 	l1 := NewLabel("l1", "This")
69 | 	l2 := NewLabel("l2", "is")
70 | 	l3 := NewLabel("l3", "a")
71 | 	l4 := NewLabel("l4", "flow\nlayout")
72 | 	l5 := NewLabel("l5", "!")
73 | 	fl := gocui.ManagerFunc(flowLayout)
74 | 	g.SetManager(l1, l2, l3, l4, l5, fl)
75 | 
76 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
77 | 		log.Panicln(err)
78 | 	}
79 | 
80 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
81 | 		log.Panicln(err)
82 | 	}
83 | }
84 | 
85 | func quit(g *gocui.Gui, v *gocui.View) error {
86 | 	return gocui.ErrQuit
87 | }
88 | 


--------------------------------------------------------------------------------
/_examples/goroutine.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 	"sync"
11 | 	"time"
12 | 
13 | 	"github.com/jroimartin/gocui"
14 | )
15 | 
16 | const NumGoroutines = 10
17 | 
18 | var (
19 | 	done = make(chan struct{})
20 | 	wg   sync.WaitGroup
21 | 
22 | 	mu  sync.Mutex // protects ctr
23 | 	ctr = 0
24 | )
25 | 
26 | func main() {
27 | 	g, err := gocui.NewGui(gocui.OutputNormal)
28 | 	if err != nil {
29 | 		log.Panicln(err)
30 | 	}
31 | 	defer g.Close()
32 | 
33 | 	g.SetManagerFunc(layout)
34 | 
35 | 	if err := keybindings(g); err != nil {
36 | 		log.Panicln(err)
37 | 	}
38 | 
39 | 	for i := 0; i < NumGoroutines; i++ {
40 | 		wg.Add(1)
41 | 		go counter(g)
42 | 	}
43 | 
44 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
45 | 		log.Panicln(err)
46 | 	}
47 | 
48 | 	wg.Wait()
49 | }
50 | 
51 | func layout(g *gocui.Gui) error {
52 | 	if v, err := g.SetView("ctr", 2, 2, 12, 4); err != nil {
53 | 		if err != gocui.ErrUnknownView {
54 | 			return err
55 | 		}
56 | 		fmt.Fprintln(v, "0")
57 | 	}
58 | 	return nil
59 | }
60 | 
61 | func keybindings(g *gocui.Gui) error {
62 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
63 | 		return err
64 | 	}
65 | 	return nil
66 | }
67 | 
68 | func quit(g *gocui.Gui, v *gocui.View) error {
69 | 	close(done)
70 | 	return gocui.ErrQuit
71 | }
72 | 
73 | func counter(g *gocui.Gui) {
74 | 	defer wg.Done()
75 | 
76 | 	for {
77 | 		select {
78 | 		case <-done:
79 | 			return
80 | 		case <-time.After(500 * time.Millisecond):
81 | 			mu.Lock()
82 | 			n := ctr
83 | 			ctr++
84 | 			mu.Unlock()
85 | 
86 | 			g.Update(func(g *gocui.Gui) error {
87 | 				v, err := g.View("ctr")
88 | 				if err != nil {
89 | 					return err
90 | 				}
91 | 				v.Clear()
92 | 				fmt.Fprintln(v, n)
93 | 				return nil
94 | 			})
95 | 		}
96 | 	}
97 | }
98 | 


--------------------------------------------------------------------------------
/_examples/hello.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
16 | 	if err != nil {
17 | 		log.Panicln(err)
18 | 	}
19 | 	defer g.Close()
20 | 
21 | 	g.SetManagerFunc(layout)
22 | 
23 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
24 | 		log.Panicln(err)
25 | 	}
26 | 
27 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
28 | 		log.Panicln(err)
29 | 	}
30 | }
31 | 
32 | func layout(g *gocui.Gui) error {
33 | 	maxX, maxY := g.Size()
34 | 	if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
35 | 		if err != gocui.ErrUnknownView {
36 | 			return err
37 | 		}
38 | 		fmt.Fprintln(v, "Hello world!")
39 | 	}
40 | 	return nil
41 | }
42 | 
43 | func quit(g *gocui.Gui, v *gocui.View) error {
44 | 	return gocui.ErrQuit
45 | }
46 | 


--------------------------------------------------------------------------------
/_examples/layout.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"log"
 9 | 
10 | 	"github.com/jroimartin/gocui"
11 | )
12 | 
13 | func layout(g *gocui.Gui) error {
14 | 	maxX, maxY := g.Size()
15 | 	if _, err := g.SetView("side", -1, -1, int(0.2*float32(maxX)), maxY-5); err != nil &&
16 | 		err != gocui.ErrUnknownView {
17 | 		return err
18 | 	}
19 | 	if _, err := g.SetView("main", int(0.2*float32(maxX)), -1, maxX, maxY-5); err != nil &&
20 | 		err != gocui.ErrUnknownView {
21 | 		return err
22 | 	}
23 | 	if _, err := g.SetView("cmdline", -1, maxY-5, maxX, maxY); err != nil &&
24 | 		err != gocui.ErrUnknownView {
25 | 		return err
26 | 	}
27 | 	return nil
28 | }
29 | 
30 | func quit(g *gocui.Gui, v *gocui.View) error {
31 | 	return gocui.ErrQuit
32 | }
33 | 
34 | func main() {
35 | 	g, err := gocui.NewGui(gocui.OutputNormal)
36 | 	if err != nil {
37 | 		log.Panicln(err)
38 | 	}
39 | 	defer g.Close()
40 | 
41 | 	g.SetManagerFunc(layout)
42 | 
43 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
44 | 		log.Panicln(err)
45 | 	}
46 | 
47 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
48 | 		log.Panicln(err)
49 | 	}
50 | }
51 | 


--------------------------------------------------------------------------------
/_examples/mask.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2015 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
16 | 	if err != nil {
17 | 		log.Fatalln(err)
18 | 	}
19 | 	defer g.Close()
20 | 
21 | 	g.Cursor = true
22 | 
23 | 	g.SetManagerFunc(layout)
24 | 
25 | 	if err := initKeybindings(g); err != nil {
26 | 		log.Fatalln(err)
27 | 	}
28 | 
29 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
30 | 		log.Fatalln(err)
31 | 	}
32 | }
33 | 
34 | func layout(g *gocui.Gui) error {
35 | 	maxX, maxY := g.Size()
36 | 
37 | 	if v, err := g.SetView("help", maxX-23, 0, maxX-1, 3); err != nil {
38 | 		if err != gocui.ErrUnknownView {
39 | 			return err
40 | 		}
41 | 		v.Title = "Keybindings"
42 | 		fmt.Fprintln(v, "^a: Set mask")
43 | 		fmt.Fprintln(v, "^c: Exit")
44 | 	}
45 | 
46 | 	if v, err := g.SetView("input", 0, 0, maxX-24, maxY-1); err != nil {
47 | 		if err != gocui.ErrUnknownView {
48 | 			return err
49 | 		}
50 | 		if _, err := g.SetCurrentView("input"); err != nil {
51 | 			return err
52 | 		}
53 | 		v.Editable = true
54 | 		v.Wrap = true
55 | 	}
56 | 
57 | 	return nil
58 | }
59 | 
60 | func initKeybindings(g *gocui.Gui) error {
61 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
62 | 		func(g *gocui.Gui, v *gocui.View) error {
63 | 			return gocui.ErrQuit
64 | 		}); err != nil {
65 | 		return err
66 | 	}
67 | 	if err := g.SetKeybinding("input", gocui.KeyCtrlA, gocui.ModNone,
68 | 		func(g *gocui.Gui, v *gocui.View) error {
69 | 			v.Mask ^= '*'
70 | 			return nil
71 | 		}); err != nil {
72 | 		return err
73 | 	}
74 | 	return nil
75 | }
76 | 


--------------------------------------------------------------------------------
/_examples/mouse.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"fmt"
  9 | 	"log"
 10 | 
 11 | 	"github.com/jroimartin/gocui"
 12 | )
 13 | 
 14 | func main() {
 15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 16 | 	if err != nil {
 17 | 		log.Panicln(err)
 18 | 	}
 19 | 	defer g.Close()
 20 | 
 21 | 	g.Cursor = true
 22 | 	g.Mouse = true
 23 | 
 24 | 	g.SetManagerFunc(layout)
 25 | 
 26 | 	if err := keybindings(g); err != nil {
 27 | 		log.Panicln(err)
 28 | 	}
 29 | 
 30 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
 31 | 		log.Panicln(err)
 32 | 	}
 33 | }
 34 | 
 35 | func layout(g *gocui.Gui) error {
 36 | 	if v, err := g.SetView("but1", 2, 2, 22, 7); err != nil {
 37 | 		if err != gocui.ErrUnknownView {
 38 | 			return err
 39 | 		}
 40 | 		v.Highlight = true
 41 | 		v.SelBgColor = gocui.ColorGreen
 42 | 		v.SelFgColor = gocui.ColorBlack
 43 | 		fmt.Fprintln(v, "Button 1 - line 1")
 44 | 		fmt.Fprintln(v, "Button 1 - line 2")
 45 | 		fmt.Fprintln(v, "Button 1 - line 3")
 46 | 		fmt.Fprintln(v, "Button 1 - line 4")
 47 | 	}
 48 | 	if v, err := g.SetView("but2", 24, 2, 44, 4); err != nil {
 49 | 		if err != gocui.ErrUnknownView {
 50 | 			return err
 51 | 		}
 52 | 		v.Highlight = true
 53 | 		v.SelBgColor = gocui.ColorGreen
 54 | 		v.SelFgColor = gocui.ColorBlack
 55 | 		fmt.Fprintln(v, "Button 2 - line 1")
 56 | 	}
 57 | 	return nil
 58 | }
 59 | 
 60 | func keybindings(g *gocui.Gui) error {
 61 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
 62 | 		return err
 63 | 	}
 64 | 	for _, n := range []string{"but1", "but2"} {
 65 | 		if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil {
 66 | 			return err
 67 | 		}
 68 | 	}
 69 | 	if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, delMsg); err != nil {
 70 | 		return err
 71 | 	}
 72 | 	return nil
 73 | }
 74 | 
 75 | func quit(g *gocui.Gui, v *gocui.View) error {
 76 | 	return gocui.ErrQuit
 77 | }
 78 | 
 79 | func showMsg(g *gocui.Gui, v *gocui.View) error {
 80 | 	var l string
 81 | 	var err error
 82 | 
 83 | 	if _, err := g.SetCurrentView(v.Name()); err != nil {
 84 | 		return err
 85 | 	}
 86 | 
 87 | 	_, cy := v.Cursor()
 88 | 	if l, err = v.Line(cy); err != nil {
 89 | 		l = ""
 90 | 	}
 91 | 
 92 | 	maxX, maxY := g.Size()
 93 | 	if v, err := g.SetView("msg", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2); err != nil {
 94 | 		if err != gocui.ErrUnknownView {
 95 | 			return err
 96 | 		}
 97 | 		fmt.Fprintln(v, l)
 98 | 	}
 99 | 	return nil
100 | }
101 | 
102 | func delMsg(g *gocui.Gui, v *gocui.View) error {
103 | 	if err := g.DeleteView("msg"); err != nil {
104 | 		return err
105 | 	}
106 | 	return nil
107 | }
108 | 


--------------------------------------------------------------------------------
/_examples/ontop.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
16 | 	if err != nil {
17 | 		log.Panicln(err)
18 | 	}
19 | 	defer g.Close()
20 | 
21 | 	g.SetManagerFunc(layout)
22 | 
23 | 	if err := keybindings(g); err != nil {
24 | 		log.Panicln(err)
25 | 	}
26 | 
27 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
28 | 		log.Panicln(err)
29 | 	}
30 | }
31 | 
32 | func layout(g *gocui.Gui) error {
33 | 	if v, err := g.SetView("v1", 10, 2, 30, 6); err != nil {
34 | 		if err != gocui.ErrUnknownView {
35 | 			return err
36 | 		}
37 | 		fmt.Fprintln(v, "View #1")
38 | 	}
39 | 	if v, err := g.SetView("v2", 20, 4, 40, 8); err != nil {
40 | 		if err != gocui.ErrUnknownView {
41 | 			return err
42 | 		}
43 | 		fmt.Fprintln(v, "View #2")
44 | 	}
45 | 	if v, err := g.SetView("v3", 30, 6, 50, 10); err != nil {
46 | 		if err != gocui.ErrUnknownView {
47 | 			return err
48 | 		}
49 | 		fmt.Fprintln(v, "View #3")
50 | 	}
51 | 
52 | 	return nil
53 | }
54 | 
55 | func keybindings(g *gocui.Gui) error {
56 | 	err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
57 | 		return gocui.ErrQuit
58 | 	})
59 | 	if err != nil {
60 | 		return err
61 | 	}
62 | 
63 | 	err = g.SetKeybinding("", '1', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
64 | 		_, err := g.SetViewOnTop("v1")
65 | 		return err
66 | 	})
67 | 	if err != nil {
68 | 		return err
69 | 	}
70 | 
71 | 	err = g.SetKeybinding("", '2', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
72 | 		_, err := g.SetViewOnTop("v2")
73 | 		return err
74 | 	})
75 | 	if err != nil {
76 | 		return err
77 | 	}
78 | 
79 | 	err = g.SetKeybinding("", '3', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
80 | 		_, err := g.SetViewOnTop("v3")
81 | 		return err
82 | 	})
83 | 	if err != nil {
84 | 		return err
85 | 	}
86 | 
87 | 	return nil
88 | }
89 | 


--------------------------------------------------------------------------------
/_examples/overlap.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"log"
 9 | 
10 | 	"github.com/jroimartin/gocui"
11 | )
12 | 
13 | func layout(g *gocui.Gui) error {
14 | 	maxX, maxY := g.Size()
15 | 	if _, err := g.SetView("v1", -1, -1, 10, 10); err != nil &&
16 | 		err != gocui.ErrUnknownView {
17 | 		return err
18 | 	}
19 | 	if _, err := g.SetView("v2", maxX-10, -1, maxX, 10); err != nil &&
20 | 		err != gocui.ErrUnknownView {
21 | 		return err
22 | 	}
23 | 	if _, err := g.SetView("v3", maxX/2-5, -1, maxX/2+5, 10); err != nil &&
24 | 		err != gocui.ErrUnknownView {
25 | 		return err
26 | 	}
27 | 	if _, err := g.SetView("v4", -1, maxY/2-5, 10, maxY/2+5); err != nil &&
28 | 		err != gocui.ErrUnknownView {
29 | 		return err
30 | 	}
31 | 	if _, err := g.SetView("v5", maxX-10, maxY/2-5, maxX, maxY/2+5); err != nil &&
32 | 		err != gocui.ErrUnknownView {
33 | 		return err
34 | 	}
35 | 	if _, err := g.SetView("v6", -1, maxY-10, 10, maxY); err != nil &&
36 | 		err != gocui.ErrUnknownView {
37 | 		return err
38 | 	}
39 | 	if _, err := g.SetView("v7", maxX-10, maxY-10, maxX, maxY); err != nil &&
40 | 		err != gocui.ErrUnknownView {
41 | 		return err
42 | 	}
43 | 	if _, err := g.SetView("v8", maxX/2-5, maxY-10, maxX/2+5, maxY); err != nil &&
44 | 		err != gocui.ErrUnknownView {
45 | 		return err
46 | 	}
47 | 	if _, err := g.SetView("v9", maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5); err != nil &&
48 | 		err != gocui.ErrUnknownView {
49 | 		return err
50 | 	}
51 | 	return nil
52 | }
53 | 
54 | func quit(g *gocui.Gui, v *gocui.View) error {
55 | 	return gocui.ErrQuit
56 | }
57 | 
58 | func main() {
59 | 	g, err := gocui.NewGui(gocui.OutputNormal)
60 | 	if err != nil {
61 | 		log.Panicln(err)
62 | 	}
63 | 	defer g.Close()
64 | 
65 | 	g.SetManagerFunc(layout)
66 | 
67 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
68 | 		log.Panicln(err)
69 | 	}
70 | 
71 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
72 | 		log.Panicln(err)
73 | 	}
74 | }
75 | 


--------------------------------------------------------------------------------
/_examples/size.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 
11 | 	"github.com/jroimartin/gocui"
12 | )
13 | 
14 | func main() {
15 | 	g, err := gocui.NewGui(gocui.OutputNormal)
16 | 	if err != nil {
17 | 		log.Panicln(err)
18 | 	}
19 | 	defer g.Close()
20 | 
21 | 	g.SetManagerFunc(layout)
22 | 
23 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
24 | 		log.Panicln(err)
25 | 	}
26 | 
27 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
28 | 		log.Panicln(err)
29 | 	}
30 | }
31 | 
32 | func layout(g *gocui.Gui) error {
33 | 	maxX, maxY := g.Size()
34 | 	v, err := g.SetView("size", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2)
35 | 	if err != nil && err != gocui.ErrUnknownView {
36 | 		return err
37 | 	}
38 | 	v.Clear()
39 | 	fmt.Fprintf(v, "%d, %d", maxX, maxY)
40 | 	return nil
41 | }
42 | 
43 | func quit(g *gocui.Gui, v *gocui.View) error {
44 | 	return gocui.ErrQuit
45 | }
46 | 


--------------------------------------------------------------------------------
/_examples/stdin.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2015 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"encoding/hex"
  9 | 	"fmt"
 10 | 	"io"
 11 | 	"log"
 12 | 	"os"
 13 | 
 14 | 	"github.com/jroimartin/gocui"
 15 | )
 16 | 
 17 | func main() {
 18 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 19 | 	if err != nil {
 20 | 		log.Fatalln(err)
 21 | 	}
 22 | 	defer g.Close()
 23 | 
 24 | 	g.Cursor = true
 25 | 
 26 | 	g.SetManagerFunc(layout)
 27 | 
 28 | 	if err := initKeybindings(g); err != nil {
 29 | 		log.Fatalln(err)
 30 | 	}
 31 | 
 32 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
 33 | 		log.Fatalln(err)
 34 | 	}
 35 | }
 36 | 
 37 | func layout(g *gocui.Gui) error {
 38 | 	maxX, _ := g.Size()
 39 | 
 40 | 	if v, err := g.SetView("help", maxX-23, 0, maxX-1, 5); err != nil {
 41 | 		if err != gocui.ErrUnknownView {
 42 | 			return err
 43 | 		}
 44 | 		fmt.Fprintln(v, "KEYBINDINGS")
 45 | 		fmt.Fprintln(v, "↑ ↓: Seek input")
 46 | 		fmt.Fprintln(v, "a: Enable autoscroll")
 47 | 		fmt.Fprintln(v, "^C: Exit")
 48 | 	}
 49 | 
 50 | 	if v, err := g.SetView("stdin", 0, 0, 80, 35); err != nil {
 51 | 		if err != gocui.ErrUnknownView {
 52 | 			return err
 53 | 		}
 54 | 		if _, err := g.SetCurrentView("stdin"); err != nil {
 55 | 			return err
 56 | 		}
 57 | 		dumper := hex.Dumper(v)
 58 | 		if _, err := io.Copy(dumper, os.Stdin); err != nil {
 59 | 			return err
 60 | 		}
 61 | 		v.Wrap = true
 62 | 	}
 63 | 
 64 | 	return nil
 65 | }
 66 | 
 67 | func initKeybindings(g *gocui.Gui) error {
 68 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
 69 | 		return err
 70 | 	}
 71 | 	if err := g.SetKeybinding("stdin", 'a', gocui.ModNone, autoscroll); err != nil {
 72 | 		return err
 73 | 	}
 74 | 	if err := g.SetKeybinding("stdin", gocui.KeyArrowUp, gocui.ModNone,
 75 | 		func(g *gocui.Gui, v *gocui.View) error {
 76 | 			scrollView(v, -1)
 77 | 			return nil
 78 | 		}); err != nil {
 79 | 		return err
 80 | 	}
 81 | 	if err := g.SetKeybinding("stdin", gocui.KeyArrowDown, gocui.ModNone,
 82 | 		func(g *gocui.Gui, v *gocui.View) error {
 83 | 			scrollView(v, 1)
 84 | 			return nil
 85 | 		}); err != nil {
 86 | 		return err
 87 | 	}
 88 | 	return nil
 89 | }
 90 | 
 91 | func quit(g *gocui.Gui, v *gocui.View) error {
 92 | 	return gocui.ErrQuit
 93 | }
 94 | 
 95 | func autoscroll(g *gocui.Gui, v *gocui.View) error {
 96 | 	v.Autoscroll = true
 97 | 	return nil
 98 | }
 99 | 
100 | func scrollView(v *gocui.View, dy int) error {
101 | 	if v != nil {
102 | 		v.Autoscroll = false
103 | 		ox, oy := v.Origin()
104 | 		if err := v.SetOrigin(ox, oy+dy); err != nil {
105 | 			return err
106 | 		}
107 | 	}
108 | 	return nil
109 | }
110 | 


--------------------------------------------------------------------------------
/_examples/title.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"log"
  9 | 
 10 | 	"github.com/jroimartin/gocui"
 11 | )
 12 | 
 13 | func main() {
 14 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 15 | 	if err != nil {
 16 | 		log.Panicln(err)
 17 | 	}
 18 | 	defer g.Close()
 19 | 
 20 | 	g.SetManagerFunc(layout)
 21 | 
 22 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
 23 | 		log.Panicln(err)
 24 | 	}
 25 | 
 26 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
 27 | 		log.Panicln(err)
 28 | 	}
 29 | }
 30 | 
 31 | func quit(g *gocui.Gui, v *gocui.View) error {
 32 | 	return gocui.ErrQuit
 33 | }
 34 | 
 35 | func layout(g *gocui.Gui) error {
 36 | 	maxX, maxY := g.Size()
 37 | 
 38 | 	// Overlap (front)
 39 | 	if v, err := g.SetView("v1", 10, 2, 30, 6); err != nil {
 40 | 		if err != gocui.ErrUnknownView {
 41 | 			return err
 42 | 		}
 43 | 		v.Title = "Regular title"
 44 | 	}
 45 | 	if v, err := g.SetView("v2", 20, 4, 40, 8); err != nil {
 46 | 		if err != gocui.ErrUnknownView {
 47 | 			return err
 48 | 		}
 49 | 		v.Title = "Regular title"
 50 | 	}
 51 | 
 52 | 	// Overlap (back)
 53 | 	if v, err := g.SetView("v3", 60, 4, 80, 8); err != nil {
 54 | 		if err != gocui.ErrUnknownView {
 55 | 			return err
 56 | 		}
 57 | 		v.Title = "Regular title"
 58 | 	}
 59 | 	if v, err := g.SetView("v4", 50, 2, 70, 6); err != nil {
 60 | 		if err != gocui.ErrUnknownView {
 61 | 			return err
 62 | 		}
 63 | 		v.Title = "Regular title"
 64 | 	}
 65 | 
 66 | 	// Overlap (frame)
 67 | 	if v, err := g.SetView("v15", 90, 2, 110, 5); err != nil {
 68 | 		if err != gocui.ErrUnknownView {
 69 | 			return err
 70 | 		}
 71 | 		v.Title = "Regular title"
 72 | 	}
 73 | 	if v, err := g.SetView("v16", 100, 5, 120, 8); err != nil {
 74 | 		if err != gocui.ErrUnknownView {
 75 | 			return err
 76 | 		}
 77 | 		v.Title = "Regular title"
 78 | 	}
 79 | 	if v, err := g.SetView("v17", 140, 5, 160, 8); err != nil {
 80 | 		if err != gocui.ErrUnknownView {
 81 | 			return err
 82 | 		}
 83 | 		v.Title = "Regular title"
 84 | 	}
 85 | 	if v, err := g.SetView("v18", 130, 2, 150, 5); err != nil {
 86 | 		if err != gocui.ErrUnknownView {
 87 | 			return err
 88 | 		}
 89 | 		v.Title = "Regular title"
 90 | 	}
 91 | 
 92 | 	// Long title
 93 | 	if v, err := g.SetView("v5", 10, 12, 30, 16); err != nil {
 94 | 		if err != gocui.ErrUnknownView {
 95 | 			return err
 96 | 		}
 97 | 		v.Title = "Long long long long title"
 98 | 	}
 99 | 
100 | 	// No title
101 | 	if v, err := g.SetView("v6", 35, 12, 55, 16); err != nil {
102 | 		if err != gocui.ErrUnknownView {
103 | 			return err
104 | 		}
105 | 		v.Title = ""
106 | 	}
107 | 	if _, err := g.SetView("v7", 60, 12, 80, 16); err != nil {
108 | 		if err != gocui.ErrUnknownView {
109 | 			return err
110 | 		}
111 | 	}
112 | 
113 | 	// Small view
114 | 	if v, err := g.SetView("v8", 85, 12, 88, 16); err != nil {
115 | 		if err != gocui.ErrUnknownView {
116 | 			return err
117 | 		}
118 | 		v.Title = "Regular title"
119 | 	}
120 | 
121 | 	// Screen borders
122 | 	if v, err := g.SetView("v9", -10, 20, 10, 24); err != nil {
123 | 		if err != gocui.ErrUnknownView {
124 | 			return err
125 | 		}
126 | 		v.Title = "Regular title"
127 | 	}
128 | 	if v, err := g.SetView("v10", maxX-10, 20, maxX+10, 24); err != nil {
129 | 		if err != gocui.ErrUnknownView {
130 | 			return err
131 | 		}
132 | 		v.Title = "Regular title"
133 | 	}
134 | 
135 | 	// Out of screen
136 | 	if v, err := g.SetView("v11", -21, 28, -1, 32); err != nil {
137 | 		if err != gocui.ErrUnknownView {
138 | 			return err
139 | 		}
140 | 		v.Title = "Regular title"
141 | 	}
142 | 	if v, err := g.SetView("v12", maxX, 28, maxX+20, 32); err != nil {
143 | 		if err != gocui.ErrUnknownView {
144 | 			return err
145 | 		}
146 | 		v.Title = "Regular title"
147 | 	}
148 | 	if v, err := g.SetView("v13", 10, -7, 30, -1); err != nil {
149 | 		if err != gocui.ErrUnknownView {
150 | 			return err
151 | 		}
152 | 		v.Title = "Regular title"
153 | 	}
154 | 	if v, err := g.SetView("v14", 10, maxY, 30, maxY+6); err != nil {
155 | 		if err != gocui.ErrUnknownView {
156 | 			return err
157 | 		}
158 | 		v.Title = "Regular title"
159 | 	}
160 | 
161 | 	return nil
162 | }
163 | 


--------------------------------------------------------------------------------
/_examples/widgets.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package main
  6 | 
  7 | import (
  8 | 	"errors"
  9 | 	"fmt"
 10 | 	"log"
 11 | 	"strings"
 12 | 
 13 | 	"github.com/jroimartin/gocui"
 14 | )
 15 | 
 16 | const delta = 0.2
 17 | 
 18 | type HelpWidget struct {
 19 | 	name string
 20 | 	x, y int
 21 | 	w, h int
 22 | 	body string
 23 | }
 24 | 
 25 | func NewHelpWidget(name string, x, y int, body string) *HelpWidget {
 26 | 	lines := strings.Split(body, "\n")
 27 | 
 28 | 	w := 0
 29 | 	for _, l := range lines {
 30 | 		if len(l) > w {
 31 | 			w = len(l)
 32 | 		}
 33 | 	}
 34 | 	h := len(lines) + 1
 35 | 	w = w + 1
 36 | 
 37 | 	return &HelpWidget{name: name, x: x, y: y, w: w, h: h, body: body}
 38 | }
 39 | 
 40 | func (w *HelpWidget) Layout(g *gocui.Gui) error {
 41 | 	v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+w.h)
 42 | 	if err != nil {
 43 | 		if err != gocui.ErrUnknownView {
 44 | 			return err
 45 | 		}
 46 | 		fmt.Fprint(v, w.body)
 47 | 	}
 48 | 	return nil
 49 | }
 50 | 
 51 | type StatusbarWidget struct {
 52 | 	name string
 53 | 	x, y int
 54 | 	w    int
 55 | 	val  float64
 56 | }
 57 | 
 58 | func NewStatusbarWidget(name string, x, y, w int) *StatusbarWidget {
 59 | 	return &StatusbarWidget{name: name, x: x, y: y, w: w}
 60 | }
 61 | 
 62 | func (w *StatusbarWidget) SetVal(val float64) error {
 63 | 	if val < 0 || val > 1 {
 64 | 		return errors.New("invalid value")
 65 | 	}
 66 | 	w.val = val
 67 | 	return nil
 68 | }
 69 | 
 70 | func (w *StatusbarWidget) Val() float64 {
 71 | 	return w.val
 72 | }
 73 | 
 74 | func (w *StatusbarWidget) Layout(g *gocui.Gui) error {
 75 | 	v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+2)
 76 | 	if err != nil && err != gocui.ErrUnknownView {
 77 | 		return err
 78 | 	}
 79 | 	v.Clear()
 80 | 
 81 | 	rep := int(w.val * float64(w.w-1))
 82 | 	fmt.Fprint(v, strings.Repeat("▒", rep))
 83 | 	return nil
 84 | }
 85 | 
 86 | type ButtonWidget struct {
 87 | 	name    string
 88 | 	x, y    int
 89 | 	w       int
 90 | 	label   string
 91 | 	handler func(g *gocui.Gui, v *gocui.View) error
 92 | }
 93 | 
 94 | func NewButtonWidget(name string, x, y int, label string, handler func(g *gocui.Gui, v *gocui.View) error) *ButtonWidget {
 95 | 	return &ButtonWidget{name: name, x: x, y: y, w: len(label) + 1, label: label, handler: handler}
 96 | }
 97 | 
 98 | func (w *ButtonWidget) Layout(g *gocui.Gui) error {
 99 | 	v, err := g.SetView(w.name, w.x, w.y, w.x+w.w, w.y+2)
100 | 	if err != nil {
101 | 		if err != gocui.ErrUnknownView {
102 | 			return err
103 | 		}
104 | 		if _, err := g.SetCurrentView(w.name); err != nil {
105 | 			return err
106 | 		}
107 | 		if err := g.SetKeybinding(w.name, gocui.KeyEnter, gocui.ModNone, w.handler); err != nil {
108 | 			return err
109 | 		}
110 | 		fmt.Fprint(v, w.label)
111 | 	}
112 | 	return nil
113 | }
114 | 
115 | func main() {
116 | 	g, err := gocui.NewGui(gocui.OutputNormal)
117 | 	if err != nil {
118 | 		log.Panicln(err)
119 | 	}
120 | 	defer g.Close()
121 | 
122 | 	g.Highlight = true
123 | 	g.SelFgColor = gocui.ColorRed
124 | 
125 | 	help := NewHelpWidget("help", 1, 1, helpText)
126 | 	status := NewStatusbarWidget("status", 1, 7, 50)
127 | 	butdown := NewButtonWidget("butdown", 52, 7, "DOWN", statusDown(status))
128 | 	butup := NewButtonWidget("butup", 58, 7, "UP", statusUp(status))
129 | 	g.SetManager(help, status, butdown, butup)
130 | 
131 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
132 | 		log.Panicln(err)
133 | 	}
134 | 	if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, toggleButton); err != nil {
135 | 		log.Panicln(err)
136 | 	}
137 | 
138 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
139 | 		log.Panicln(err)
140 | 	}
141 | }
142 | 
143 | func quit(g *gocui.Gui, v *gocui.View) error {
144 | 	return gocui.ErrQuit
145 | }
146 | 
147 | func toggleButton(g *gocui.Gui, v *gocui.View) error {
148 | 	nextview := "butdown"
149 | 	if v != nil && v.Name() == "butdown" {
150 | 		nextview = "butup"
151 | 	}
152 | 	_, err := g.SetCurrentView(nextview)
153 | 	return err
154 | }
155 | 
156 | func statusUp(status *StatusbarWidget) func(g *gocui.Gui, v *gocui.View) error {
157 | 	return func(g *gocui.Gui, v *gocui.View) error {
158 | 		return statusSet(status, delta)
159 | 	}
160 | }
161 | 
162 | func statusDown(status *StatusbarWidget) func(g *gocui.Gui, v *gocui.View) error {
163 | 	return func(g *gocui.Gui, v *gocui.View) error {
164 | 		return statusSet(status, -delta)
165 | 	}
166 | }
167 | 
168 | func statusSet(sw *StatusbarWidget, inc float64) error {
169 | 	val := sw.Val() + inc
170 | 	if val < 0 || val > 1 {
171 | 		return nil
172 | 	}
173 | 	return sw.SetVal(val)
174 | }
175 | 
176 | const helpText = `KEYBINDINGS
177 | Tab: Move between buttons
178 | Enter: Push button
179 | ^C: Exit`
180 | 


--------------------------------------------------------------------------------
/_examples/wrap.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package main
 6 | 
 7 | import (
 8 | 	"fmt"
 9 | 	"log"
10 | 	"strings"
11 | 
12 | 	"github.com/jroimartin/gocui"
13 | )
14 | 
15 | func layout(g *gocui.Gui) error {
16 | 	maxX, maxY := g.Size()
17 | 	if v, err := g.SetView("main", 1, 1, maxX-1, maxY-1); err != nil {
18 | 		if err != gocui.ErrUnknownView {
19 | 			return err
20 | 		}
21 | 		v.Wrap = true
22 | 
23 | 		line := strings.Repeat("This is a long line -- ", 10)
24 | 		fmt.Fprintf(v, "%s\n\n", line)
25 | 		fmt.Fprintln(v, "Short")
26 | 	}
27 | 	return nil
28 | }
29 | 
30 | func quit(g *gocui.Gui, v *gocui.View) error {
31 | 	return gocui.ErrQuit
32 | }
33 | 
34 | func main() {
35 | 	g, err := gocui.NewGui(gocui.OutputNormal)
36 | 	if err != nil {
37 | 		log.Panicln(err)
38 | 	}
39 | 	defer g.Close()
40 | 
41 | 	g.SetManagerFunc(layout)
42 | 
43 | 	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
44 | 		log.Panicln(err)
45 | 	}
46 | 
47 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
48 | 		log.Panicln(err)
49 | 	}
50 | }
51 | 


--------------------------------------------------------------------------------
/attribute.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2014 The gocui Authors. All rights reserved.
 2 | // Use of this source code is governed by a BSD-style
 3 | // license that can be found in the LICENSE file.
 4 | 
 5 | package gocui
 6 | 
 7 | import "github.com/nsf/termbox-go"
 8 | 
 9 | // Attribute represents a terminal attribute, like color, font style, etc. They
10 | // can be combined using bitwise OR (|). Note that it is not possible to
11 | // combine multiple color attributes.
12 | type Attribute termbox.Attribute
13 | 
14 | // Color attributes.
15 | const (
16 | 	ColorDefault Attribute = Attribute(termbox.ColorDefault)
17 | 	ColorBlack             = Attribute(termbox.ColorBlack)
18 | 	ColorRed               = Attribute(termbox.ColorRed)
19 | 	ColorGreen             = Attribute(termbox.ColorGreen)
20 | 	ColorYellow            = Attribute(termbox.ColorYellow)
21 | 	ColorBlue              = Attribute(termbox.ColorBlue)
22 | 	ColorMagenta           = Attribute(termbox.ColorMagenta)
23 | 	ColorCyan              = Attribute(termbox.ColorCyan)
24 | 	ColorWhite             = Attribute(termbox.ColorWhite)
25 | )
26 | 
27 | // Text style attributes.
28 | const (
29 | 	AttrBold      Attribute = Attribute(termbox.AttrBold)
30 | 	AttrUnderline           = Attribute(termbox.AttrUnderline)
31 | 	AttrReverse             = Attribute(termbox.AttrReverse)
32 | )
33 | 


--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | /*
  6 | Package gocui allows to create console user interfaces.
  7 | 
  8 | Create a new GUI:
  9 | 
 10 | 	g, err := gocui.NewGui(gocui.OutputNormal)
 11 | 	if err != nil {
 12 | 		// handle error
 13 | 	}
 14 | 	defer g.Close()
 15 | 
 16 | 	// Set GUI managers and key bindings
 17 | 	// ...
 18 | 
 19 | 	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
 20 | 		// handle error
 21 | 	}
 22 | 
 23 | Set GUI managers:
 24 | 
 25 | 	g.SetManager(mgr1, mgr2)
 26 | 
 27 | Managers are in charge of GUI's layout and can be used to build widgets. On
 28 | each iteration of the GUI's main loop, the Layout function of each configured
 29 | manager is executed. Managers are used to set-up and update the application's
 30 | main views, being possible to freely change them during execution. Also, it is
 31 | important to mention that a main loop iteration is executed on each reported
 32 | event (key-press, mouse event, window resize, etc).
 33 | 
 34 | GUIs are composed by Views, you can think of it as buffers. Views implement the
 35 | io.ReadWriter interface, so you can just write to them if you want to modify
 36 | their content. The same is valid for reading.
 37 | 
 38 | Create and initialize a view with absolute coordinates:
 39 | 
 40 | 	if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil {
 41 | 		if err != gocui.ErrUnknownView {
 42 | 			// handle error
 43 | 		}
 44 | 		fmt.Fprintln(v, "This is a new view")
 45 | 		// ...
 46 | 	}
 47 | 
 48 | Views can also be created using relative coordinates:
 49 | 
 50 | 	maxX, maxY := g.Size()
 51 | 	if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
 52 | 		// ...
 53 | 	}
 54 | 
 55 | Configure keybindings:
 56 | 
 57 | 	if err := g.SetKeybinding("viewname", gocui.KeyEnter, gocui.ModNone, fcn); err != nil {
 58 | 		// handle error
 59 | 	}
 60 | 
 61 | gocui implements full mouse support that can be enabled with:
 62 | 
 63 | 	g.Mouse = true
 64 | 
 65 | Mouse events are handled like any other keybinding:
 66 | 
 67 | 	if err := g.SetKeybinding("viewname", gocui.MouseLeft, gocui.ModNone, fcn); err != nil {
 68 | 		// handle error
 69 | 	}
 70 | 
 71 | IMPORTANT: Views can only be created, destroyed or updated in three ways: from
 72 | the Layout function within managers, from keybinding callbacks or via
 73 | *Gui.Update(). The reason for this is that it allows gocui to be
 74 | concurrent-safe. So, if you want to update your GUI from a goroutine, you must
 75 | use *Gui.Update(). For example:
 76 | 
 77 | 	g.Update(func(g *gocui.Gui) error {
 78 | 		v, err := g.View("viewname")
 79 | 		if err != nil {
 80 | 			// handle error
 81 | 		}
 82 | 		v.Clear()
 83 | 		fmt.Fprintln(v, "Writing from different goroutines")
 84 | 		return nil
 85 | 	})
 86 | 
 87 | By default, gocui provides a basic edition mode. This mode can be extended
 88 | and customized creating a new Editor and assigning it to *View.Editor:
 89 | 
 90 | 	type Editor interface {
 91 | 		Edit(v *View, key Key, ch rune, mod Modifier)
 92 | 	}
 93 | 
 94 | DefaultEditor can be taken as example to create your own custom Editor:
 95 | 
 96 | 	var DefaultEditor Editor = EditorFunc(simpleEditor)
 97 | 
 98 | 	func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
 99 | 		switch {
100 | 		case ch != 0 && mod == 0:
101 | 			v.EditWrite(ch)
102 | 		case key == KeySpace:
103 | 			v.EditWrite(' ')
104 | 		case key == KeyBackspace || key == KeyBackspace2:
105 | 			v.EditDelete(true)
106 | 		// ...
107 | 		}
108 | 	}
109 | 
110 | Colored text:
111 | 
112 | Views allow to add colored text using ANSI colors. For example:
113 | 
114 | 	fmt.Fprintln(v, "\x1b[0;31mHello world")
115 | 
116 | For more information, see the examples in folder "_examples/".
117 | */
118 | package gocui
119 | 


--------------------------------------------------------------------------------
/edit.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package gocui
  6 | 
  7 | import "errors"
  8 | 
  9 | const maxInt = int(^uint(0) >> 1)
 10 | 
 11 | // Editor interface must be satisfied by gocui editors.
 12 | type Editor interface {
 13 | 	Edit(v *View, key Key, ch rune, mod Modifier)
 14 | }
 15 | 
 16 | // The EditorFunc type is an adapter to allow the use of ordinary functions as
 17 | // Editors. If f is a function with the appropriate signature, EditorFunc(f)
 18 | // is an Editor object that calls f.
 19 | type EditorFunc func(v *View, key Key, ch rune, mod Modifier)
 20 | 
 21 | // Edit calls f(v, key, ch, mod)
 22 | func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) {
 23 | 	f(v, key, ch, mod)
 24 | }
 25 | 
 26 | // DefaultEditor is the default editor.
 27 | var DefaultEditor Editor = EditorFunc(simpleEditor)
 28 | 
 29 | // simpleEditor is used as the default gocui editor.
 30 | func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
 31 | 	switch {
 32 | 	case ch != 0 && mod == 0:
 33 | 		v.EditWrite(ch)
 34 | 	case key == KeySpace:
 35 | 		v.EditWrite(' ')
 36 | 	case key == KeyBackspace || key == KeyBackspace2:
 37 | 		v.EditDelete(true)
 38 | 	case key == KeyDelete:
 39 | 		v.EditDelete(false)
 40 | 	case key == KeyInsert:
 41 | 		v.Overwrite = !v.Overwrite
 42 | 	case key == KeyEnter:
 43 | 		v.EditNewLine()
 44 | 	case key == KeyArrowDown:
 45 | 		v.MoveCursor(0, 1, false)
 46 | 	case key == KeyArrowUp:
 47 | 		v.MoveCursor(0, -1, false)
 48 | 	case key == KeyArrowLeft:
 49 | 		v.MoveCursor(-1, 0, false)
 50 | 	case key == KeyArrowRight:
 51 | 		v.MoveCursor(1, 0, false)
 52 | 	}
 53 | }
 54 | 
 55 | // EditWrite writes a rune at the cursor position.
 56 | func (v *View) EditWrite(ch rune) {
 57 | 	v.writeRune(v.cx, v.cy, ch)
 58 | 	v.MoveCursor(1, 0, true)
 59 | }
 60 | 
 61 | // EditDelete deletes a rune at the cursor position. back determines the
 62 | // direction.
 63 | func (v *View) EditDelete(back bool) {
 64 | 	x, y := v.ox+v.cx, v.oy+v.cy
 65 | 	if y < 0 {
 66 | 		return
 67 | 	} else if y >= len(v.viewLines) {
 68 | 		v.MoveCursor(-1, 0, true)
 69 | 		return
 70 | 	}
 71 | 
 72 | 	maxX, _ := v.Size()
 73 | 	if back {
 74 | 		if x == 0 { // start of the line
 75 | 			if y < 1 {
 76 | 				return
 77 | 			}
 78 | 
 79 | 			var maxPrevWidth int
 80 | 			if v.Wrap {
 81 | 				maxPrevWidth = maxX
 82 | 			} else {
 83 | 				maxPrevWidth = maxInt
 84 | 			}
 85 | 
 86 | 			if v.viewLines[y].linesX == 0 { // regular line
 87 | 				v.mergeLines(v.cy - 1)
 88 | 				if len(v.viewLines[y-1].line) < maxPrevWidth {
 89 | 					v.MoveCursor(-1, 0, true)
 90 | 				}
 91 | 			} else { // wrapped line
 92 | 				v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
 93 | 				v.MoveCursor(-1, 0, true)
 94 | 			}
 95 | 		} else { // middle/end of the line
 96 | 			v.deleteRune(v.cx-1, v.cy)
 97 | 			v.MoveCursor(-1, 0, true)
 98 | 		}
 99 | 	} else {
100 | 		if x == len(v.viewLines[y].line) { // end of the line
101 | 			v.mergeLines(v.cy)
102 | 		} else { // start/middle of the line
103 | 			v.deleteRune(v.cx, v.cy)
104 | 		}
105 | 	}
106 | }
107 | 
108 | // EditNewLine inserts a new line under the cursor.
109 | func (v *View) EditNewLine() {
110 | 	v.breakLine(v.cx, v.cy)
111 | 	v.ox = 0
112 | 	v.cx = 0
113 | 	v.MoveCursor(0, 1, true)
114 | }
115 | 
116 | // MoveCursor moves the cursor taking into account the width of the line/view,
117 | // displacing the origin if necessary.
118 | func (v *View) MoveCursor(dx, dy int, writeMode bool) {
119 | 	maxX, maxY := v.Size()
120 | 	cx, cy := v.cx+dx, v.cy+dy
121 | 	x, y := v.ox+cx, v.oy+cy
122 | 
123 | 	var curLineWidth, prevLineWidth int
124 | 	// get the width of the current line
125 | 	if writeMode {
126 | 		if v.Wrap {
127 | 			curLineWidth = maxX - 1
128 | 		} else {
129 | 			curLineWidth = maxInt
130 | 		}
131 | 	} else {
132 | 		if y >= 0 && y < len(v.viewLines) {
133 | 			curLineWidth = len(v.viewLines[y].line)
134 | 			if v.Wrap && curLineWidth >= maxX {
135 | 				curLineWidth = maxX - 1
136 | 			}
137 | 		} else {
138 | 			curLineWidth = 0
139 | 		}
140 | 	}
141 | 	// get the width of the previous line
142 | 	if y-1 >= 0 && y-1 < len(v.viewLines) {
143 | 		prevLineWidth = len(v.viewLines[y-1].line)
144 | 	} else {
145 | 		prevLineWidth = 0
146 | 	}
147 | 
148 | 	// adjust cursor's x position and view's x origin
149 | 	if x > curLineWidth { // move to next line
150 | 		if dx > 0 { // horizontal movement
151 | 			cy++
152 | 			if writeMode || v.oy+cy < len(v.viewLines) {
153 | 				if !v.Wrap {
154 | 					v.ox = 0
155 | 				}
156 | 				v.cx = 0
157 | 			}
158 | 		} else { // vertical movement
159 | 			if curLineWidth > 0 { // move cursor to the EOL
160 | 				if v.Wrap {
161 | 					v.cx = curLineWidth
162 | 				} else {
163 | 					ncx := curLineWidth - v.ox
164 | 					if ncx < 0 {
165 | 						v.ox += ncx
166 | 						if v.ox < 0 {
167 | 							v.ox = 0
168 | 						}
169 | 						v.cx = 0
170 | 					} else {
171 | 						v.cx = ncx
172 | 					}
173 | 				}
174 | 			} else {
175 | 				if writeMode || v.oy+cy < len(v.viewLines) {
176 | 					if !v.Wrap {
177 | 						v.ox = 0
178 | 					}
179 | 					v.cx = 0
180 | 				}
181 | 			}
182 | 		}
183 | 	} else if cx < 0 {
184 | 		if !v.Wrap && v.ox > 0 { // move origin to the left
185 | 			v.ox += cx
186 | 			v.cx = 0
187 | 		} else { // move to previous line
188 | 			cy--
189 | 			if prevLineWidth > 0 {
190 | 				if !v.Wrap { // set origin so the EOL is visible
191 | 					nox := prevLineWidth - maxX + 1
192 | 					if nox < 0 {
193 | 						v.ox = 0
194 | 					} else {
195 | 						v.ox = nox
196 | 					}
197 | 				}
198 | 				v.cx = prevLineWidth
199 | 			} else {
200 | 				if !v.Wrap {
201 | 					v.ox = 0
202 | 				}
203 | 				v.cx = 0
204 | 			}
205 | 		}
206 | 	} else { // stay on the same line
207 | 		if v.Wrap {
208 | 			v.cx = cx
209 | 		} else {
210 | 			if cx >= maxX {
211 | 				v.ox += cx - maxX + 1
212 | 				v.cx = maxX
213 | 			} else {
214 | 				v.cx = cx
215 | 			}
216 | 		}
217 | 	}
218 | 
219 | 	// adjust cursor's y position and view's y origin
220 | 	if cy < 0 {
221 | 		if v.oy > 0 {
222 | 			v.oy--
223 | 		}
224 | 	} else if writeMode || v.oy+cy < len(v.viewLines) {
225 | 		if cy >= maxY {
226 | 			v.oy++
227 | 		} else {
228 | 			v.cy = cy
229 | 		}
230 | 	}
231 | }
232 | 
233 | // writeRune writes a rune into the view's internal buffer, at the
234 | // position corresponding to the point (x, y). The length of the internal
235 | // buffer is increased if the point is out of bounds. Overwrite mode is
236 | // governed by the value of View.overwrite.
237 | func (v *View) writeRune(x, y int, ch rune) error {
238 | 	v.tainted = true
239 | 
240 | 	x, y, err := v.realPosition(x, y)
241 | 	if err != nil {
242 | 		return err
243 | 	}
244 | 
245 | 	if x < 0 || y < 0 {
246 | 		return errors.New("invalid point")
247 | 	}
248 | 
249 | 	if y >= len(v.lines) {
250 | 		s := make([][]cell, y-len(v.lines)+1)
251 | 		v.lines = append(v.lines, s...)
252 | 	}
253 | 
254 | 	olen := len(v.lines[y])
255 | 
256 | 	var s []cell
257 | 	if x >= len(v.lines[y]) {
258 | 		s = make([]cell, x-len(v.lines[y])+1)
259 | 	} else if !v.Overwrite {
260 | 		s = make([]cell, 1)
261 | 	}
262 | 	v.lines[y] = append(v.lines[y], s...)
263 | 
264 | 	if !v.Overwrite || (v.Overwrite && x >= olen-1) {
265 | 		copy(v.lines[y][x+1:], v.lines[y][x:])
266 | 	}
267 | 	v.lines[y][x] = cell{
268 | 		fgColor: v.FgColor,
269 | 		bgColor: v.BgColor,
270 | 		chr:     ch,
271 | 	}
272 | 
273 | 	return nil
274 | }
275 | 
276 | // deleteRune removes a rune from the view's internal buffer, at the
277 | // position corresponding to the point (x, y).
278 | func (v *View) deleteRune(x, y int) error {
279 | 	v.tainted = true
280 | 
281 | 	x, y, err := v.realPosition(x, y)
282 | 	if err != nil {
283 | 		return err
284 | 	}
285 | 
286 | 	if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
287 | 		return errors.New("invalid point")
288 | 	}
289 | 	v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
290 | 	return nil
291 | }
292 | 
293 | // mergeLines merges the lines "y" and "y+1" if possible.
294 | func (v *View) mergeLines(y int) error {
295 | 	v.tainted = true
296 | 
297 | 	_, y, err := v.realPosition(0, y)
298 | 	if err != nil {
299 | 		return err
300 | 	}
301 | 
302 | 	if y < 0 || y >= len(v.lines) {
303 | 		return errors.New("invalid point")
304 | 	}
305 | 
306 | 	if y < len(v.lines)-1 { // otherwise we don't need to merge anything
307 | 		v.lines[y] = append(v.lines[y], v.lines[y+1]...)
308 | 		v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
309 | 	}
310 | 	return nil
311 | }
312 | 
313 | // breakLine breaks a line of the internal buffer at the position corresponding
314 | // to the point (x, y).
315 | func (v *View) breakLine(x, y int) error {
316 | 	v.tainted = true
317 | 
318 | 	x, y, err := v.realPosition(x, y)
319 | 	if err != nil {
320 | 		return err
321 | 	}
322 | 
323 | 	if y < 0 || y >= len(v.lines) {
324 | 		return errors.New("invalid point")
325 | 	}
326 | 
327 | 	var left, right []cell
328 | 	if x < len(v.lines[y]) { // break line
329 | 		left = make([]cell, len(v.lines[y][:x]))
330 | 		copy(left, v.lines[y][:x])
331 | 		right = make([]cell, len(v.lines[y][x:]))
332 | 		copy(right, v.lines[y][x:])
333 | 	} else { // new empty line
334 | 		left = v.lines[y]
335 | 	}
336 | 
337 | 	lines := make([][]cell, len(v.lines)+1)
338 | 	lines[y] = left
339 | 	lines[y+1] = right
340 | 	copy(lines, v.lines[:y])
341 | 	copy(lines[y+2:], v.lines[y+1:])
342 | 	v.lines = lines
343 | 	return nil
344 | }
345 | 


--------------------------------------------------------------------------------
/escape.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package gocui
  6 | 
  7 | import (
  8 | 	"errors"
  9 | 	"strconv"
 10 | )
 11 | 
 12 | type escapeInterpreter struct {
 13 | 	state                  escapeState
 14 | 	curch                  rune
 15 | 	csiParam               []string
 16 | 	curFgColor, curBgColor Attribute
 17 | 	mode                   OutputMode
 18 | }
 19 | 
 20 | type escapeState int
 21 | 
 22 | const (
 23 | 	stateNone escapeState = iota
 24 | 	stateEscape
 25 | 	stateCSI
 26 | 	stateParams
 27 | )
 28 | 
 29 | var (
 30 | 	errNotCSI        = errors.New("Not a CSI escape sequence")
 31 | 	errCSIParseError = errors.New("CSI escape sequence parsing error")
 32 | 	errCSITooLong    = errors.New("CSI escape sequence is too long")
 33 | )
 34 | 
 35 | // runes in case of error will output the non-parsed runes as a string.
 36 | func (ei *escapeInterpreter) runes() []rune {
 37 | 	switch ei.state {
 38 | 	case stateNone:
 39 | 		return []rune{0x1b}
 40 | 	case stateEscape:
 41 | 		return []rune{0x1b, ei.curch}
 42 | 	case stateCSI:
 43 | 		return []rune{0x1b, '[', ei.curch}
 44 | 	case stateParams:
 45 | 		ret := []rune{0x1b, '['}
 46 | 		for _, s := range ei.csiParam {
 47 | 			ret = append(ret, []rune(s)...)
 48 | 			ret = append(ret, ';')
 49 | 		}
 50 | 		return append(ret, ei.curch)
 51 | 	}
 52 | 	return nil
 53 | }
 54 | 
 55 | // newEscapeInterpreter returns an escapeInterpreter that will be able to parse
 56 | // terminal escape sequences.
 57 | func newEscapeInterpreter(mode OutputMode) *escapeInterpreter {
 58 | 	ei := &escapeInterpreter{
 59 | 		state:      stateNone,
 60 | 		curFgColor: ColorDefault,
 61 | 		curBgColor: ColorDefault,
 62 | 		mode:       mode,
 63 | 	}
 64 | 	return ei
 65 | }
 66 | 
 67 | // reset sets the escapeInterpreter in initial state.
 68 | func (ei *escapeInterpreter) reset() {
 69 | 	ei.state = stateNone
 70 | 	ei.curFgColor = ColorDefault
 71 | 	ei.curBgColor = ColorDefault
 72 | 	ei.csiParam = nil
 73 | }
 74 | 
 75 | // parseOne parses a rune. If isEscape is true, it means that the rune is part
 76 | // of an escape sequence, and as such should not be printed verbatim. Otherwise,
 77 | // it's not an escape sequence.
 78 | func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
 79 | 	// Sanity checks
 80 | 	if len(ei.csiParam) > 20 {
 81 | 		return false, errCSITooLong
 82 | 	}
 83 | 	if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 {
 84 | 		return false, errCSITooLong
 85 | 	}
 86 | 
 87 | 	ei.curch = ch
 88 | 
 89 | 	switch ei.state {
 90 | 	case stateNone:
 91 | 		if ch == 0x1b {
 92 | 			ei.state = stateEscape
 93 | 			return true, nil
 94 | 		}
 95 | 		return false, nil
 96 | 	case stateEscape:
 97 | 		if ch == '[' {
 98 | 			ei.state = stateCSI
 99 | 			return true, nil
100 | 		}
101 | 		return false, errNotCSI
102 | 	case stateCSI:
103 | 		switch {
104 | 		case ch >= '0' && ch <= '9':
105 | 			ei.csiParam = append(ei.csiParam, "")
106 | 		case ch == 'm':
107 | 			ei.csiParam = append(ei.csiParam, "0")
108 | 		default:
109 | 			return false, errCSIParseError
110 | 		}
111 | 		ei.state = stateParams
112 | 		fallthrough
113 | 	case stateParams:
114 | 		switch {
115 | 		case ch >= '0' && ch <= '9':
116 | 			ei.csiParam[len(ei.csiParam)-1] += string(ch)
117 | 			return true, nil
118 | 		case ch == ';':
119 | 			ei.csiParam = append(ei.csiParam, "")
120 | 			return true, nil
121 | 		case ch == 'm':
122 | 			var err error
123 | 			switch ei.mode {
124 | 			case OutputNormal:
125 | 				err = ei.outputNormal()
126 | 			case Output256:
127 | 				err = ei.output256()
128 | 			}
129 | 			if err != nil {
130 | 				return false, errCSIParseError
131 | 			}
132 | 
133 | 			ei.state = stateNone
134 | 			ei.csiParam = nil
135 | 			return true, nil
136 | 		default:
137 | 			return false, errCSIParseError
138 | 		}
139 | 	}
140 | 	return false, nil
141 | }
142 | 
143 | // outputNormal provides 8 different colors:
144 | //   black, red, green, yellow, blue, magenta, cyan, white
145 | func (ei *escapeInterpreter) outputNormal() error {
146 | 	for _, param := range ei.csiParam {
147 | 		p, err := strconv.Atoi(param)
148 | 		if err != nil {
149 | 			return errCSIParseError
150 | 		}
151 | 
152 | 		switch {
153 | 		case p >= 30 && p <= 37:
154 | 			ei.curFgColor = Attribute(p - 30 + 1)
155 | 		case p == 39:
156 | 			ei.curFgColor = ColorDefault
157 | 		case p >= 40 && p <= 47:
158 | 			ei.curBgColor = Attribute(p - 40 + 1)
159 | 		case p == 49:
160 | 			ei.curBgColor = ColorDefault
161 | 		case p == 1:
162 | 			ei.curFgColor |= AttrBold
163 | 		case p == 4:
164 | 			ei.curFgColor |= AttrUnderline
165 | 		case p == 7:
166 | 			ei.curFgColor |= AttrReverse
167 | 		case p == 0:
168 | 			ei.curFgColor = ColorDefault
169 | 			ei.curBgColor = ColorDefault
170 | 		}
171 | 	}
172 | 
173 | 	return nil
174 | }
175 | 
176 | // output256 allows you to leverage the 256-colors terminal mode:
177 | //   0x01 - 0x08: the 8 colors as in OutputNormal
178 | //   0x09 - 0x10: Color* | AttrBold
179 | //   0x11 - 0xe8: 216 different colors
180 | //   0xe9 - 0x1ff: 24 different shades of grey
181 | func (ei *escapeInterpreter) output256() error {
182 | 	if len(ei.csiParam) < 3 {
183 | 		return ei.outputNormal()
184 | 	}
185 | 
186 | 	mode, err := strconv.Atoi(ei.csiParam[1])
187 | 	if err != nil {
188 | 		return errCSIParseError
189 | 	}
190 | 	if mode != 5 {
191 | 		return ei.outputNormal()
192 | 	}
193 | 
194 | 	fgbg, err := strconv.Atoi(ei.csiParam[0])
195 | 	if err != nil {
196 | 		return errCSIParseError
197 | 	}
198 | 	color, err := strconv.Atoi(ei.csiParam[2])
199 | 	if err != nil {
200 | 		return errCSIParseError
201 | 	}
202 | 
203 | 	switch fgbg {
204 | 	case 38:
205 | 		ei.curFgColor = Attribute(color + 1)
206 | 
207 | 		for _, param := range ei.csiParam[3:] {
208 | 			p, err := strconv.Atoi(param)
209 | 			if err != nil {
210 | 				return errCSIParseError
211 | 			}
212 | 
213 | 			switch {
214 | 			case p == 1:
215 | 				ei.curFgColor |= AttrBold
216 | 			case p == 4:
217 | 				ei.curFgColor |= AttrUnderline
218 | 			case p == 7:
219 | 				ei.curFgColor |= AttrReverse
220 | 			}
221 | 		}
222 | 	case 48:
223 | 		ei.curBgColor = Attribute(color + 1)
224 | 	default:
225 | 		return errCSIParseError
226 | 	}
227 | 
228 | 	return nil
229 | }
230 | 


--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jroimartin/gocui
2 | 
3 | go 1.16
4 | 
5 | require github.com/nsf/termbox-go v1.1.1
6 | 


--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
2 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
3 | github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
4 | github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
5 | 


--------------------------------------------------------------------------------
/gui.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package gocui
  6 | 
  7 | import (
  8 | 	"errors"
  9 | 
 10 | 	"github.com/nsf/termbox-go"
 11 | )
 12 | 
 13 | var (
 14 | 	// ErrQuit is used to decide if the MainLoop finished successfully.
 15 | 	ErrQuit = errors.New("quit")
 16 | 
 17 | 	// ErrUnknownView allows to assert if a View must be initialized.
 18 | 	ErrUnknownView = errors.New("unknown view")
 19 | )
 20 | 
 21 | // OutputMode represents the terminal's output mode (8 or 256 colors).
 22 | type OutputMode termbox.OutputMode
 23 | 
 24 | const (
 25 | 	// OutputNormal provides 8-colors terminal mode.
 26 | 	OutputNormal = OutputMode(termbox.OutputNormal)
 27 | 
 28 | 	// Output256 provides 256-colors terminal mode.
 29 | 	Output256 = OutputMode(termbox.Output256)
 30 | )
 31 | 
 32 | // Gui represents the whole User Interface, including the views, layouts
 33 | // and keybindings.
 34 | type Gui struct {
 35 | 	tbEvents    chan termbox.Event
 36 | 	userEvents  chan userEvent
 37 | 	views       []*View
 38 | 	currentView *View
 39 | 	managers    []Manager
 40 | 	keybindings []*keybinding
 41 | 	maxX, maxY  int
 42 | 	outputMode  OutputMode
 43 | 
 44 | 	// BgColor and FgColor allow to configure the background and foreground
 45 | 	// colors of the GUI.
 46 | 	BgColor, FgColor Attribute
 47 | 
 48 | 	// SelBgColor and SelFgColor allow to configure the background and
 49 | 	// foreground colors of the frame of the current view.
 50 | 	SelBgColor, SelFgColor Attribute
 51 | 
 52 | 	// If Highlight is true, Sel{Bg,Fg}Colors will be used to draw the
 53 | 	// frame of the current view.
 54 | 	Highlight bool
 55 | 
 56 | 	// If Cursor is true then the cursor is enabled.
 57 | 	Cursor bool
 58 | 
 59 | 	// If Mouse is true then mouse events will be enabled.
 60 | 	Mouse bool
 61 | 
 62 | 	// If InputEsc is true, when ESC sequence is in the buffer and it doesn't
 63 | 	// match any known sequence, ESC means KeyEsc.
 64 | 	InputEsc bool
 65 | 
 66 | 	// If ASCII is true then use ASCII instead of unicode to draw the
 67 | 	// interface. Using ASCII is more portable.
 68 | 	ASCII bool
 69 | }
 70 | 
 71 | // NewGui returns a new Gui object with a given output mode.
 72 | func NewGui(mode OutputMode) (*Gui, error) {
 73 | 	if err := termbox.Init(); err != nil {
 74 | 		return nil, err
 75 | 	}
 76 | 
 77 | 	g := &Gui{}
 78 | 
 79 | 	g.outputMode = mode
 80 | 	termbox.SetOutputMode(termbox.OutputMode(mode))
 81 | 
 82 | 	g.tbEvents = make(chan termbox.Event, 20)
 83 | 	g.userEvents = make(chan userEvent, 20)
 84 | 
 85 | 	g.maxX, g.maxY = termbox.Size()
 86 | 
 87 | 	g.BgColor, g.FgColor = ColorDefault, ColorDefault
 88 | 	g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault
 89 | 
 90 | 	return g, nil
 91 | }
 92 | 
 93 | // Close finalizes the library. It should be called after a successful
 94 | // initialization and when gocui is not needed anymore.
 95 | func (g *Gui) Close() {
 96 | 	termbox.Close()
 97 | }
 98 | 
 99 | // Size returns the terminal's size.
100 | func (g *Gui) Size() (x, y int) {
101 | 	return g.maxX, g.maxY
102 | }
103 | 
104 | // SetRune writes a rune at the given point, relative to the top-left
105 | // corner of the terminal. It checks if the position is valid and applies
106 | // the given colors.
107 | func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
108 | 	if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
109 | 		return errors.New("invalid point")
110 | 	}
111 | 	termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
112 | 	return nil
113 | }
114 | 
115 | // Rune returns the rune contained in the cell at the given position.
116 | // It checks if the position is valid.
117 | func (g *Gui) Rune(x, y int) (rune, error) {
118 | 	if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
119 | 		return ' ', errors.New("invalid point")
120 | 	}
121 | 	c := termbox.CellBuffer()[y*g.maxX+x]
122 | 	return c.Ch, nil
123 | }
124 | 
125 | // SetView creates a new view with its top-left corner at (x0, y0)
126 | // and the bottom-right one at (x1, y1). If a view with the same name
127 | // already exists, its dimensions are updated; otherwise, the error
128 | // ErrUnknownView is returned, which allows to assert if the View must
129 | // be initialized. It checks if the position is valid.
130 | func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) {
131 | 	if x0 >= x1 || y0 >= y1 {
132 | 		return nil, errors.New("invalid dimensions")
133 | 	}
134 | 	if name == "" {
135 | 		return nil, errors.New("invalid name")
136 | 	}
137 | 
138 | 	if v, err := g.View(name); err == nil {
139 | 		v.x0 = x0
140 | 		v.y0 = y0
141 | 		v.x1 = x1
142 | 		v.y1 = y1
143 | 		v.tainted = true
144 | 		return v, nil
145 | 	}
146 | 
147 | 	v := newView(name, x0, y0, x1, y1, g.outputMode)
148 | 	v.BgColor, v.FgColor = g.BgColor, g.FgColor
149 | 	v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor
150 | 	g.views = append(g.views, v)
151 | 	return v, ErrUnknownView
152 | }
153 | 
154 | // SetViewOnTop sets the given view on top of the existing ones.
155 | func (g *Gui) SetViewOnTop(name string) (*View, error) {
156 | 	for i, v := range g.views {
157 | 		if v.name == name {
158 | 			s := append(g.views[:i], g.views[i+1:]...)
159 | 			g.views = append(s, v)
160 | 			return v, nil
161 | 		}
162 | 	}
163 | 	return nil, ErrUnknownView
164 | }
165 | 
166 | // SetViewOnBottom sets the given view on bottom of the existing ones.
167 | func (g *Gui) SetViewOnBottom(name string) (*View, error) {
168 | 	for i, v := range g.views {
169 | 		if v.name == name {
170 | 			s := append(g.views[:i], g.views[i+1:]...)
171 | 			g.views = append([]*View{v}, s...)
172 | 			return v, nil
173 | 		}
174 | 	}
175 | 	return nil, ErrUnknownView
176 | }
177 | 
178 | // Views returns all the views in the GUI.
179 | func (g *Gui) Views() []*View {
180 | 	return g.views
181 | }
182 | 
183 | // View returns a pointer to the view with the given name, or error
184 | // ErrUnknownView if a view with that name does not exist.
185 | func (g *Gui) View(name string) (*View, error) {
186 | 	for _, v := range g.views {
187 | 		if v.name == name {
188 | 			return v, nil
189 | 		}
190 | 	}
191 | 	return nil, ErrUnknownView
192 | }
193 | 
194 | // ViewByPosition returns a pointer to a view matching the given position, or
195 | // error ErrUnknownView if a view in that position does not exist.
196 | func (g *Gui) ViewByPosition(x, y int) (*View, error) {
197 | 	// traverse views in reverse order checking top views first
198 | 	for i := len(g.views); i > 0; i-- {
199 | 		v := g.views[i-1]
200 | 		if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
201 | 			return v, nil
202 | 		}
203 | 	}
204 | 	return nil, ErrUnknownView
205 | }
206 | 
207 | // ViewPosition returns the coordinates of the view with the given name, or
208 | // error ErrUnknownView if a view with that name does not exist.
209 | func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) {
210 | 	for _, v := range g.views {
211 | 		if v.name == name {
212 | 			return v.x0, v.y0, v.x1, v.y1, nil
213 | 		}
214 | 	}
215 | 	return 0, 0, 0, 0, ErrUnknownView
216 | }
217 | 
218 | // DeleteView deletes a view by name.
219 | func (g *Gui) DeleteView(name string) error {
220 | 	for i, v := range g.views {
221 | 		if v.name == name {
222 | 			g.views = append(g.views[:i], g.views[i+1:]...)
223 | 			return nil
224 | 		}
225 | 	}
226 | 	return ErrUnknownView
227 | }
228 | 
229 | // SetCurrentView gives the focus to a given view.
230 | func (g *Gui) SetCurrentView(name string) (*View, error) {
231 | 	for _, v := range g.views {
232 | 		if v.name == name {
233 | 			g.currentView = v
234 | 			return v, nil
235 | 		}
236 | 	}
237 | 	return nil, ErrUnknownView
238 | }
239 | 
240 | // CurrentView returns the currently focused view, or nil if no view
241 | // owns the focus.
242 | func (g *Gui) CurrentView() *View {
243 | 	return g.currentView
244 | }
245 | 
246 | // SetKeybinding creates a new keybinding. If viewname equals to ""
247 | // (empty string) then the keybinding will apply to all views. key must
248 | // be a rune or a Key.
249 | func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, handler func(*Gui, *View) error) error {
250 | 	var kb *keybinding
251 | 
252 | 	k, ch, err := getKey(key)
253 | 	if err != nil {
254 | 		return err
255 | 	}
256 | 	kb = newKeybinding(viewname, k, ch, mod, handler)
257 | 	g.keybindings = append(g.keybindings, kb)
258 | 	return nil
259 | }
260 | 
261 | // DeleteKeybinding deletes a keybinding.
262 | func (g *Gui) DeleteKeybinding(viewname string, key interface{}, mod Modifier) error {
263 | 	k, ch, err := getKey(key)
264 | 	if err != nil {
265 | 		return err
266 | 	}
267 | 
268 | 	for i, kb := range g.keybindings {
269 | 		if kb.viewName == viewname && kb.ch == ch && kb.key == k && kb.mod == mod {
270 | 			g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...)
271 | 			return nil
272 | 		}
273 | 	}
274 | 	return errors.New("keybinding not found")
275 | }
276 | 
277 | // DeleteKeybindings deletes all keybindings of view.
278 | func (g *Gui) DeleteKeybindings(viewname string) {
279 | 	var s []*keybinding
280 | 	for _, kb := range g.keybindings {
281 | 		if kb.viewName != viewname {
282 | 			s = append(s, kb)
283 | 		}
284 | 	}
285 | 	g.keybindings = s
286 | }
287 | 
288 | // getKey takes an empty interface with a key and returns the corresponding
289 | // typed Key or rune.
290 | func getKey(key interface{}) (Key, rune, error) {
291 | 	switch t := key.(type) {
292 | 	case Key:
293 | 		return t, 0, nil
294 | 	case rune:
295 | 		return 0, t, nil
296 | 	default:
297 | 		return 0, 0, errors.New("unknown type")
298 | 	}
299 | }
300 | 
301 | // userEvent represents an event triggered by the user.
302 | type userEvent struct {
303 | 	f func(*Gui) error
304 | }
305 | 
306 | // Update executes the passed function. This method can be called safely from a
307 | // goroutine in order to update the GUI. It is important to note that the
308 | // passed function won't be executed immediately, instead it will be added to
309 | // the user events queue. Given that Update spawns a goroutine, the order in
310 | // which the user events will be handled is not guaranteed.
311 | func (g *Gui) Update(f func(*Gui) error) {
312 | 	go func() { g.userEvents <- userEvent{f: f} }()
313 | }
314 | 
315 | // A Manager is in charge of GUI's layout and can be used to build widgets.
316 | type Manager interface {
317 | 	// Layout is called every time the GUI is redrawn, it must contain the
318 | 	// base views and its initializations.
319 | 	Layout(*Gui) error
320 | }
321 | 
322 | // The ManagerFunc type is an adapter to allow the use of ordinary functions as
323 | // Managers. If f is a function with the appropriate signature, ManagerFunc(f)
324 | // is an Manager object that calls f.
325 | type ManagerFunc func(*Gui) error
326 | 
327 | // Layout calls f(g)
328 | func (f ManagerFunc) Layout(g *Gui) error {
329 | 	return f(g)
330 | }
331 | 
332 | // SetManager sets the given GUI managers. It deletes all views and
333 | // keybindings.
334 | func (g *Gui) SetManager(managers ...Manager) {
335 | 	g.managers = managers
336 | 	g.currentView = nil
337 | 	g.views = nil
338 | 	g.keybindings = nil
339 | 
340 | 	go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
341 | }
342 | 
343 | // SetManagerFunc sets the given manager function. It deletes all views and
344 | // keybindings.
345 | func (g *Gui) SetManagerFunc(manager func(*Gui) error) {
346 | 	g.SetManager(ManagerFunc(manager))
347 | }
348 | 
349 | // MainLoop runs the main loop until an error is returned. A successful
350 | // finish should return ErrQuit.
351 | func (g *Gui) MainLoop() error {
352 | 	go func() {
353 | 		for {
354 | 			g.tbEvents <- termbox.PollEvent()
355 | 		}
356 | 	}()
357 | 
358 | 	inputMode := termbox.InputAlt
359 | 	if g.InputEsc {
360 | 		inputMode = termbox.InputEsc
361 | 	}
362 | 	if g.Mouse {
363 | 		inputMode |= termbox.InputMouse
364 | 	}
365 | 	termbox.SetInputMode(inputMode)
366 | 
367 | 	if err := g.flush(); err != nil {
368 | 		return err
369 | 	}
370 | 	for {
371 | 		select {
372 | 		case ev := <-g.tbEvents:
373 | 			if err := g.handleEvent(&ev); err != nil {
374 | 				return err
375 | 			}
376 | 		case ev := <-g.userEvents:
377 | 			if err := ev.f(g); err != nil {
378 | 				return err
379 | 			}
380 | 		}
381 | 		if err := g.consumeevents(); err != nil {
382 | 			return err
383 | 		}
384 | 		if err := g.flush(); err != nil {
385 | 			return err
386 | 		}
387 | 	}
388 | }
389 | 
390 | // consumeevents handles the remaining events in the events pool.
391 | func (g *Gui) consumeevents() error {
392 | 	for {
393 | 		select {
394 | 		case ev := <-g.tbEvents:
395 | 			if err := g.handleEvent(&ev); err != nil {
396 | 				return err
397 | 			}
398 | 		case ev := <-g.userEvents:
399 | 			if err := ev.f(g); err != nil {
400 | 				return err
401 | 			}
402 | 		default:
403 | 			return nil
404 | 		}
405 | 	}
406 | }
407 | 
408 | // handleEvent handles an event, based on its type (key-press, error,
409 | // etc.)
410 | func (g *Gui) handleEvent(ev *termbox.Event) error {
411 | 	switch ev.Type {
412 | 	case termbox.EventKey, termbox.EventMouse:
413 | 		return g.onKey(ev)
414 | 	case termbox.EventError:
415 | 		return ev.Err
416 | 	default:
417 | 		return nil
418 | 	}
419 | }
420 | 
421 | // flush updates the gui, re-drawing frames and buffers.
422 | func (g *Gui) flush() error {
423 | 	termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor))
424 | 
425 | 	maxX, maxY := termbox.Size()
426 | 	// if GUI's size has changed, we need to redraw all views
427 | 	if maxX != g.maxX || maxY != g.maxY {
428 | 		for _, v := range g.views {
429 | 			v.tainted = true
430 | 		}
431 | 	}
432 | 	g.maxX, g.maxY = maxX, maxY
433 | 
434 | 	for _, m := range g.managers {
435 | 		if err := m.Layout(g); err != nil {
436 | 			return err
437 | 		}
438 | 	}
439 | 	for _, v := range g.views {
440 | 		if v.Frame {
441 | 			var fgColor, bgColor Attribute
442 | 			if g.Highlight && v == g.currentView {
443 | 				fgColor = g.SelFgColor
444 | 				bgColor = g.SelBgColor
445 | 			} else {
446 | 				fgColor = g.FgColor
447 | 				bgColor = g.BgColor
448 | 			}
449 | 
450 | 			if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
451 | 				return err
452 | 			}
453 | 			if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil {
454 | 				return err
455 | 			}
456 | 			if v.Title != "" {
457 | 				if err := g.drawTitle(v, fgColor, bgColor); err != nil {
458 | 					return err
459 | 				}
460 | 			}
461 | 		}
462 | 		if err := g.draw(v); err != nil {
463 | 			return err
464 | 		}
465 | 	}
466 | 	termbox.Flush()
467 | 	return nil
468 | }
469 | 
470 | // drawFrameEdges draws the horizontal and vertical edges of a view.
471 | func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
472 | 	runeH, runeV := '─', '│'
473 | 	if g.ASCII {
474 | 		runeH, runeV = '-', '|'
475 | 	}
476 | 
477 | 	for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ {
478 | 		if x < 0 {
479 | 			continue
480 | 		}
481 | 		if v.y0 > -1 && v.y0 < g.maxY {
482 | 			if err := g.SetRune(x, v.y0, runeH, fgColor, bgColor); err != nil {
483 | 				return err
484 | 			}
485 | 		}
486 | 		if v.y1 > -1 && v.y1 < g.maxY {
487 | 			if err := g.SetRune(x, v.y1, runeH, fgColor, bgColor); err != nil {
488 | 				return err
489 | 			}
490 | 		}
491 | 	}
492 | 	for y := v.y0 + 1; y < v.y1 && y < g.maxY; y++ {
493 | 		if y < 0 {
494 | 			continue
495 | 		}
496 | 		if v.x0 > -1 && v.x0 < g.maxX {
497 | 			if err := g.SetRune(v.x0, y, runeV, fgColor, bgColor); err != nil {
498 | 				return err
499 | 			}
500 | 		}
501 | 		if v.x1 > -1 && v.x1 < g.maxX {
502 | 			if err := g.SetRune(v.x1, y, runeV, fgColor, bgColor); err != nil {
503 | 				return err
504 | 			}
505 | 		}
506 | 	}
507 | 	return nil
508 | }
509 | 
510 | // drawFrameCorners draws the corners of the view.
511 | func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error {
512 | 	runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘'
513 | 	if g.ASCII {
514 | 		runeTL, runeTR, runeBL, runeBR = '+', '+', '+', '+'
515 | 	}
516 | 
517 | 	corners := []struct {
518 | 		x, y int
519 | 		ch   rune
520 | 	}{{v.x0, v.y0, runeTL}, {v.x1, v.y0, runeTR}, {v.x0, v.y1, runeBL}, {v.x1, v.y1, runeBR}}
521 | 
522 | 	for _, c := range corners {
523 | 		if c.x >= 0 && c.y >= 0 && c.x < g.maxX && c.y < g.maxY {
524 | 			if err := g.SetRune(c.x, c.y, c.ch, fgColor, bgColor); err != nil {
525 | 				return err
526 | 			}
527 | 		}
528 | 	}
529 | 	return nil
530 | }
531 | 
532 | // drawTitle draws the title of the view.
533 | func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
534 | 	if v.y0 < 0 || v.y0 >= g.maxY {
535 | 		return nil
536 | 	}
537 | 
538 | 	for i, ch := range v.Title {
539 | 		x := v.x0 + i + 2
540 | 		if x < 0 {
541 | 			continue
542 | 		} else if x > v.x1-2 || x >= g.maxX {
543 | 			break
544 | 		}
545 | 		if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
546 | 			return err
547 | 		}
548 | 	}
549 | 	return nil
550 | }
551 | 
552 | // draw manages the cursor and calls the draw function of a view.
553 | func (g *Gui) draw(v *View) error {
554 | 	if g.Cursor {
555 | 		if curview := g.currentView; curview != nil {
556 | 			vMaxX, vMaxY := curview.Size()
557 | 			if curview.cx < 0 {
558 | 				curview.cx = 0
559 | 			} else if curview.cx >= vMaxX {
560 | 				curview.cx = vMaxX - 1
561 | 			}
562 | 			if curview.cy < 0 {
563 | 				curview.cy = 0
564 | 			} else if curview.cy >= vMaxY {
565 | 				curview.cy = vMaxY - 1
566 | 			}
567 | 
568 | 			gMaxX, gMaxY := g.Size()
569 | 			cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1
570 | 			if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY {
571 | 				termbox.SetCursor(cx, cy)
572 | 			} else {
573 | 				termbox.HideCursor()
574 | 			}
575 | 		}
576 | 	} else {
577 | 		termbox.HideCursor()
578 | 	}
579 | 
580 | 	v.clearRunes()
581 | 	if err := v.draw(); err != nil {
582 | 		return err
583 | 	}
584 | 	return nil
585 | }
586 | 
587 | // onKey manages key-press events. A keybinding handler is called when
588 | // a key-press or mouse event satisfies a configured keybinding. Furthermore,
589 | // currentView's internal buffer is modified if currentView.Editable is true.
590 | func (g *Gui) onKey(ev *termbox.Event) error {
591 | 	switch ev.Type {
592 | 	case termbox.EventKey:
593 | 		matched, err := g.execKeybindings(g.currentView, ev)
594 | 		if err != nil {
595 | 			return err
596 | 		}
597 | 		if matched {
598 | 			break
599 | 		}
600 | 		if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil {
601 | 			g.currentView.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod))
602 | 		}
603 | 	case termbox.EventMouse:
604 | 		mx, my := ev.MouseX, ev.MouseY
605 | 		v, err := g.ViewByPosition(mx, my)
606 | 		if err != nil {
607 | 			break
608 | 		}
609 | 		if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil {
610 | 			return err
611 | 		}
612 | 		if _, err := g.execKeybindings(v, ev); err != nil {
613 | 			return err
614 | 		}
615 | 	}
616 | 
617 | 	return nil
618 | }
619 | 
620 | // execKeybindings executes the keybinding handlers that match the passed view
621 | // and event. The value of matched is true if there is a match and no errors.
622 | func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
623 | 	matched = false
624 | 	for _, kb := range g.keybindings {
625 | 		if kb.handler == nil {
626 | 			continue
627 | 		}
628 | 		if kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) && kb.matchView(v) {
629 | 			if err := kb.handler(g, v); err != nil {
630 | 				return false, err
631 | 			}
632 | 			matched = true
633 | 		}
634 | 	}
635 | 	return matched, nil
636 | }
637 | 


--------------------------------------------------------------------------------
/keybinding.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package gocui
  6 | 
  7 | import "github.com/nsf/termbox-go"
  8 | 
  9 | // Keybidings are used to link a given key-press event with a handler.
 10 | type keybinding struct {
 11 | 	viewName string
 12 | 	key      Key
 13 | 	ch       rune
 14 | 	mod      Modifier
 15 | 	handler  func(*Gui, *View) error
 16 | }
 17 | 
 18 | // newKeybinding returns a new Keybinding object.
 19 | func newKeybinding(viewname string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) {
 20 | 	kb = &keybinding{
 21 | 		viewName: viewname,
 22 | 		key:      key,
 23 | 		ch:       ch,
 24 | 		mod:      mod,
 25 | 		handler:  handler,
 26 | 	}
 27 | 	return kb
 28 | }
 29 | 
 30 | // matchKeypress returns if the keybinding matches the keypress.
 31 | func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool {
 32 | 	return kb.key == key && kb.ch == ch && kb.mod == mod
 33 | }
 34 | 
 35 | // matchView returns if the keybinding matches the current view.
 36 | func (kb *keybinding) matchView(v *View) bool {
 37 | 	if kb.viewName == "" {
 38 | 		return true
 39 | 	}
 40 | 	return v != nil && kb.viewName == v.name
 41 | }
 42 | 
 43 | // Key represents special keys or keys combinations.
 44 | type Key termbox.Key
 45 | 
 46 | // Special keys.
 47 | const (
 48 | 	KeyF1         Key = Key(termbox.KeyF1)
 49 | 	KeyF2             = Key(termbox.KeyF2)
 50 | 	KeyF3             = Key(termbox.KeyF3)
 51 | 	KeyF4             = Key(termbox.KeyF4)
 52 | 	KeyF5             = Key(termbox.KeyF5)
 53 | 	KeyF6             = Key(termbox.KeyF6)
 54 | 	KeyF7             = Key(termbox.KeyF7)
 55 | 	KeyF8             = Key(termbox.KeyF8)
 56 | 	KeyF9             = Key(termbox.KeyF9)
 57 | 	KeyF10            = Key(termbox.KeyF10)
 58 | 	KeyF11            = Key(termbox.KeyF11)
 59 | 	KeyF12            = Key(termbox.KeyF12)
 60 | 	KeyInsert         = Key(termbox.KeyInsert)
 61 | 	KeyDelete         = Key(termbox.KeyDelete)
 62 | 	KeyHome           = Key(termbox.KeyHome)
 63 | 	KeyEnd            = Key(termbox.KeyEnd)
 64 | 	KeyPgup           = Key(termbox.KeyPgup)
 65 | 	KeyPgdn           = Key(termbox.KeyPgdn)
 66 | 	KeyArrowUp        = Key(termbox.KeyArrowUp)
 67 | 	KeyArrowDown      = Key(termbox.KeyArrowDown)
 68 | 	KeyArrowLeft      = Key(termbox.KeyArrowLeft)
 69 | 	KeyArrowRight     = Key(termbox.KeyArrowRight)
 70 | 
 71 | 	MouseLeft      = Key(termbox.MouseLeft)
 72 | 	MouseMiddle    = Key(termbox.MouseMiddle)
 73 | 	MouseRight     = Key(termbox.MouseRight)
 74 | 	MouseRelease   = Key(termbox.MouseRelease)
 75 | 	MouseWheelUp   = Key(termbox.MouseWheelUp)
 76 | 	MouseWheelDown = Key(termbox.MouseWheelDown)
 77 | )
 78 | 
 79 | // Keys combinations.
 80 | const (
 81 | 	KeyCtrlTilde      Key = Key(termbox.KeyCtrlTilde)
 82 | 	KeyCtrl2              = Key(termbox.KeyCtrl2)
 83 | 	KeyCtrlSpace          = Key(termbox.KeyCtrlSpace)
 84 | 	KeyCtrlA              = Key(termbox.KeyCtrlA)
 85 | 	KeyCtrlB              = Key(termbox.KeyCtrlB)
 86 | 	KeyCtrlC              = Key(termbox.KeyCtrlC)
 87 | 	KeyCtrlD              = Key(termbox.KeyCtrlD)
 88 | 	KeyCtrlE              = Key(termbox.KeyCtrlE)
 89 | 	KeyCtrlF              = Key(termbox.KeyCtrlF)
 90 | 	KeyCtrlG              = Key(termbox.KeyCtrlG)
 91 | 	KeyBackspace          = Key(termbox.KeyBackspace)
 92 | 	KeyCtrlH              = Key(termbox.KeyCtrlH)
 93 | 	KeyTab                = Key(termbox.KeyTab)
 94 | 	KeyCtrlI              = Key(termbox.KeyCtrlI)
 95 | 	KeyCtrlJ              = Key(termbox.KeyCtrlJ)
 96 | 	KeyCtrlK              = Key(termbox.KeyCtrlK)
 97 | 	KeyCtrlL              = Key(termbox.KeyCtrlL)
 98 | 	KeyEnter              = Key(termbox.KeyEnter)
 99 | 	KeyCtrlM              = Key(termbox.KeyCtrlM)
100 | 	KeyCtrlN              = Key(termbox.KeyCtrlN)
101 | 	KeyCtrlO              = Key(termbox.KeyCtrlO)
102 | 	KeyCtrlP              = Key(termbox.KeyCtrlP)
103 | 	KeyCtrlQ              = Key(termbox.KeyCtrlQ)
104 | 	KeyCtrlR              = Key(termbox.KeyCtrlR)
105 | 	KeyCtrlS              = Key(termbox.KeyCtrlS)
106 | 	KeyCtrlT              = Key(termbox.KeyCtrlT)
107 | 	KeyCtrlU              = Key(termbox.KeyCtrlU)
108 | 	KeyCtrlV              = Key(termbox.KeyCtrlV)
109 | 	KeyCtrlW              = Key(termbox.KeyCtrlW)
110 | 	KeyCtrlX              = Key(termbox.KeyCtrlX)
111 | 	KeyCtrlY              = Key(termbox.KeyCtrlY)
112 | 	KeyCtrlZ              = Key(termbox.KeyCtrlZ)
113 | 	KeyEsc                = Key(termbox.KeyEsc)
114 | 	KeyCtrlLsqBracket     = Key(termbox.KeyCtrlLsqBracket)
115 | 	KeyCtrl3              = Key(termbox.KeyCtrl3)
116 | 	KeyCtrl4              = Key(termbox.KeyCtrl4)
117 | 	KeyCtrlBackslash      = Key(termbox.KeyCtrlBackslash)
118 | 	KeyCtrl5              = Key(termbox.KeyCtrl5)
119 | 	KeyCtrlRsqBracket     = Key(termbox.KeyCtrlRsqBracket)
120 | 	KeyCtrl6              = Key(termbox.KeyCtrl6)
121 | 	KeyCtrl7              = Key(termbox.KeyCtrl7)
122 | 	KeyCtrlSlash          = Key(termbox.KeyCtrlSlash)
123 | 	KeyCtrlUnderscore     = Key(termbox.KeyCtrlUnderscore)
124 | 	KeySpace              = Key(termbox.KeySpace)
125 | 	KeyBackspace2         = Key(termbox.KeyBackspace2)
126 | 	KeyCtrl8              = Key(termbox.KeyCtrl8)
127 | )
128 | 
129 | // Modifier allows to define special keys combinations. They can be used
130 | // in combination with Keys or Runes when a new keybinding is defined.
131 | type Modifier termbox.Modifier
132 | 
133 | // Modifiers.
134 | const (
135 | 	ModNone Modifier = Modifier(0)
136 | 	ModAlt           = Modifier(termbox.ModAlt)
137 | )
138 | 


--------------------------------------------------------------------------------
/view.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 The gocui Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | package gocui
  6 | 
  7 | import (
  8 | 	"bytes"
  9 | 	"errors"
 10 | 	"io"
 11 | 	"strings"
 12 | 
 13 | 	"github.com/nsf/termbox-go"
 14 | )
 15 | 
 16 | // A View is a window. It maintains its own internal buffer and cursor
 17 | // position.
 18 | type View struct {
 19 | 	name           string
 20 | 	x0, y0, x1, y1 int
 21 | 	ox, oy         int
 22 | 	cx, cy         int
 23 | 	lines          [][]cell
 24 | 	readOffset     int
 25 | 	readCache      string
 26 | 
 27 | 	tainted   bool       // marks if the viewBuffer must be updated
 28 | 	viewLines []viewLine // internal representation of the view's buffer
 29 | 
 30 | 	ei *escapeInterpreter // used to decode ESC sequences on Write
 31 | 
 32 | 	// BgColor and FgColor allow to configure the background and foreground
 33 | 	// colors of the View.
 34 | 	BgColor, FgColor Attribute
 35 | 
 36 | 	// SelBgColor and SelFgColor are used to configure the background and
 37 | 	// foreground colors of the selected line, when it is highlighted.
 38 | 	SelBgColor, SelFgColor Attribute
 39 | 
 40 | 	// If Editable is true, keystrokes will be added to the view's internal
 41 | 	// buffer at the cursor position.
 42 | 	Editable bool
 43 | 
 44 | 	// Editor allows to define the editor that manages the edition mode,
 45 | 	// including keybindings or cursor behaviour. DefaultEditor is used by
 46 | 	// default.
 47 | 	Editor Editor
 48 | 
 49 | 	// Overwrite enables or disables the overwrite mode of the view.
 50 | 	Overwrite bool
 51 | 
 52 | 	// If Highlight is true, Sel{Bg,Fg}Colors will be used
 53 | 	// for the line under the cursor position.
 54 | 	Highlight bool
 55 | 
 56 | 	// If Frame is true, a border will be drawn around the view.
 57 | 	Frame bool
 58 | 
 59 | 	// If Wrap is true, the content that is written to this View is
 60 | 	// automatically wrapped when it is longer than its width. If true the
 61 | 	// view's x-origin will be ignored.
 62 | 	Wrap bool
 63 | 
 64 | 	// If Autoscroll is true, the View will automatically scroll down when the
 65 | 	// text overflows. If true the view's y-origin will be ignored.
 66 | 	Autoscroll bool
 67 | 
 68 | 	// If Frame is true, Title allows to configure a title for the view.
 69 | 	Title string
 70 | 
 71 | 	// If Mask is true, the View will display the mask instead of the real
 72 | 	// content
 73 | 	Mask rune
 74 | }
 75 | 
 76 | type viewLine struct {
 77 | 	linesX, linesY int // coordinates relative to v.lines
 78 | 	line           []cell
 79 | }
 80 | 
 81 | type cell struct {
 82 | 	chr              rune
 83 | 	bgColor, fgColor Attribute
 84 | }
 85 | 
 86 | type lineType []cell
 87 | 
 88 | // String returns a string from a given cell slice.
 89 | func (l lineType) String() string {
 90 | 	str := ""
 91 | 	for _, c := range l {
 92 | 		str += string(c.chr)
 93 | 	}
 94 | 	return str
 95 | }
 96 | 
 97 | // newView returns a new View object.
 98 | func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View {
 99 | 	v := &View{
100 | 		name:    name,
101 | 		x0:      x0,
102 | 		y0:      y0,
103 | 		x1:      x1,
104 | 		y1:      y1,
105 | 		Frame:   true,
106 | 		Editor:  DefaultEditor,
107 | 		tainted: true,
108 | 		ei:      newEscapeInterpreter(mode),
109 | 	}
110 | 	return v
111 | }
112 | 
113 | // Size returns the number of visible columns and rows in the View.
114 | func (v *View) Size() (x, y int) {
115 | 	return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1
116 | }
117 | 
118 | // Name returns the name of the view.
119 | func (v *View) Name() string {
120 | 	return v.name
121 | }
122 | 
123 | // setRune sets a rune at the given point relative to the view. It applies the
124 | // specified colors, taking into account if the cell must be highlighted. Also,
125 | // it checks if the position is valid.
126 | func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
127 | 	maxX, maxY := v.Size()
128 | 	if x < 0 || x >= maxX || y < 0 || y >= maxY {
129 | 		return errors.New("invalid point")
130 | 	}
131 | 
132 | 	var (
133 | 		ry, rcy int
134 | 		err     error
135 | 	)
136 | 	if v.Highlight {
137 | 		_, ry, err = v.realPosition(x, y)
138 | 		if err != nil {
139 | 			return err
140 | 		}
141 | 		_, rcy, err = v.realPosition(v.cx, v.cy)
142 | 		if err != nil {
143 | 			return err
144 | 		}
145 | 	}
146 | 
147 | 	if v.Mask != 0 {
148 | 		fgColor = v.FgColor
149 | 		bgColor = v.BgColor
150 | 		ch = v.Mask
151 | 	} else if v.Highlight && ry == rcy {
152 | 		fgColor = v.SelFgColor
153 | 		bgColor = v.SelBgColor
154 | 	}
155 | 
156 | 	termbox.SetCell(v.x0+x+1, v.y0+y+1, ch,
157 | 		termbox.Attribute(fgColor), termbox.Attribute(bgColor))
158 | 
159 | 	return nil
160 | }
161 | 
162 | // SetCursor sets the cursor position of the view at the given point,
163 | // relative to the view. It checks if the position is valid.
164 | func (v *View) SetCursor(x, y int) error {
165 | 	maxX, maxY := v.Size()
166 | 	if x < 0 || x >= maxX || y < 0 || y >= maxY {
167 | 		return errors.New("invalid point")
168 | 	}
169 | 	v.cx = x
170 | 	v.cy = y
171 | 	return nil
172 | }
173 | 
174 | // Cursor returns the cursor position of the view.
175 | func (v *View) Cursor() (x, y int) {
176 | 	return v.cx, v.cy
177 | }
178 | 
179 | // SetOrigin sets the origin position of the view's internal buffer,
180 | // so the buffer starts to be printed from this point, which means that
181 | // it is linked with the origin point of view. It can be used to
182 | // implement Horizontal and Vertical scrolling with just incrementing
183 | // or decrementing ox and oy.
184 | func (v *View) SetOrigin(x, y int) error {
185 | 	if x < 0 || y < 0 {
186 | 		return errors.New("invalid point")
187 | 	}
188 | 	v.ox = x
189 | 	v.oy = y
190 | 	return nil
191 | }
192 | 
193 | // Origin returns the origin position of the view.
194 | func (v *View) Origin() (x, y int) {
195 | 	return v.ox, v.oy
196 | }
197 | 
198 | // Write appends a byte slice into the view's internal buffer. Because
199 | // View implements the io.Writer interface, it can be passed as parameter
200 | // of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
201 | // be called to clear the view's buffer.
202 | func (v *View) Write(p []byte) (n int, err error) {
203 | 	v.tainted = true
204 | 
205 | 	for _, ch := range bytes.Runes(p) {
206 | 		switch ch {
207 | 		case '\n':
208 | 			v.lines = append(v.lines, nil)
209 | 		case '\r':
210 | 			nl := len(v.lines)
211 | 			if nl > 0 {
212 | 				v.lines[nl-1] = nil
213 | 			} else {
214 | 				v.lines = make([][]cell, 1)
215 | 			}
216 | 		default:
217 | 			cells := v.parseInput(ch)
218 | 			if cells == nil {
219 | 				continue
220 | 			}
221 | 
222 | 			nl := len(v.lines)
223 | 			if nl > 0 {
224 | 				v.lines[nl-1] = append(v.lines[nl-1], cells...)
225 | 			} else {
226 | 				v.lines = append(v.lines, cells)
227 | 			}
228 | 		}
229 | 	}
230 | 	return len(p), nil
231 | }
232 | 
233 | // parseInput parses char by char the input written to the View. It returns nil
234 | // while processing ESC sequences. Otherwise, it returns a cell slice that
235 | // contains the processed data.
236 | func (v *View) parseInput(ch rune) []cell {
237 | 	cells := []cell{}
238 | 
239 | 	isEscape, err := v.ei.parseOne(ch)
240 | 	if err != nil {
241 | 		for _, r := range v.ei.runes() {
242 | 			c := cell{
243 | 				fgColor: v.FgColor,
244 | 				bgColor: v.BgColor,
245 | 				chr:     r,
246 | 			}
247 | 			cells = append(cells, c)
248 | 		}
249 | 		v.ei.reset()
250 | 	} else {
251 | 		if isEscape {
252 | 			return nil
253 | 		}
254 | 		c := cell{
255 | 			fgColor: v.ei.curFgColor,
256 | 			bgColor: v.ei.curBgColor,
257 | 			chr:     ch,
258 | 		}
259 | 		cells = append(cells, c)
260 | 	}
261 | 
262 | 	return cells
263 | }
264 | 
265 | // Read reads data into p. It returns the number of bytes read into p.
266 | // At EOF, err will be io.EOF. Calling Read() after Rewind() makes the
267 | // cache to be refreshed with the contents of the view.
268 | func (v *View) Read(p []byte) (n int, err error) {
269 | 	if v.readOffset == 0 {
270 | 		v.readCache = v.Buffer()
271 | 	}
272 | 	if v.readOffset < len(v.readCache) {
273 | 		n = copy(p, v.readCache[v.readOffset:])
274 | 		v.readOffset += n
275 | 	} else {
276 | 		err = io.EOF
277 | 	}
278 | 	return
279 | }
280 | 
281 | // Rewind sets the offset for the next Read to 0, which also refresh the
282 | // read cache.
283 | func (v *View) Rewind() {
284 | 	v.readOffset = 0
285 | }
286 | 
287 | // draw re-draws the view's contents.
288 | func (v *View) draw() error {
289 | 	maxX, maxY := v.Size()
290 | 
291 | 	if v.Wrap {
292 | 		if maxX == 0 {
293 | 			return errors.New("X size of the view cannot be 0")
294 | 		}
295 | 		v.ox = 0
296 | 	}
297 | 	if v.tainted {
298 | 		v.viewLines = nil
299 | 		for i, line := range v.lines {
300 | 			if v.Wrap {
301 | 				if len(line) < maxX {
302 | 					vline := viewLine{linesX: 0, linesY: i, line: line}
303 | 					v.viewLines = append(v.viewLines, vline)
304 | 					continue
305 | 				} else {
306 | 					for n := 0; n <= len(line); n += maxX {
307 | 						if len(line[n:]) <= maxX {
308 | 							vline := viewLine{linesX: n, linesY: i, line: line[n:]}
309 | 							v.viewLines = append(v.viewLines, vline)
310 | 						} else {
311 | 							vline := viewLine{linesX: n, linesY: i, line: line[n : n+maxX]}
312 | 							v.viewLines = append(v.viewLines, vline)
313 | 						}
314 | 					}
315 | 				}
316 | 			} else {
317 | 				vline := viewLine{linesX: 0, linesY: i, line: line}
318 | 				v.viewLines = append(v.viewLines, vline)
319 | 			}
320 | 		}
321 | 		v.tainted = false
322 | 	}
323 | 
324 | 	if v.Autoscroll && len(v.viewLines) > maxY {
325 | 		v.oy = len(v.viewLines) - maxY
326 | 	}
327 | 	y := 0
328 | 	for i, vline := range v.viewLines {
329 | 		if i < v.oy {
330 | 			continue
331 | 		}
332 | 		if y >= maxY {
333 | 			break
334 | 		}
335 | 		x := 0
336 | 		for j, c := range vline.line {
337 | 			if j < v.ox {
338 | 				continue
339 | 			}
340 | 			if x >= maxX {
341 | 				break
342 | 			}
343 | 
344 | 			fgColor := c.fgColor
345 | 			if fgColor == ColorDefault {
346 | 				fgColor = v.FgColor
347 | 			}
348 | 			bgColor := c.bgColor
349 | 			if bgColor == ColorDefault {
350 | 				bgColor = v.BgColor
351 | 			}
352 | 
353 | 			if err := v.setRune(x, y, c.chr, fgColor, bgColor); err != nil {
354 | 				return err
355 | 			}
356 | 			x++
357 | 		}
358 | 		y++
359 | 	}
360 | 	return nil
361 | }
362 | 
363 | // realPosition returns the position in the internal buffer corresponding to the
364 | // point (x, y) of the view.
365 | func (v *View) realPosition(vx, vy int) (x, y int, err error) {
366 | 	vx = v.ox + vx
367 | 	vy = v.oy + vy
368 | 
369 | 	if vx < 0 || vy < 0 {
370 | 		return 0, 0, errors.New("invalid point")
371 | 	}
372 | 
373 | 	if len(v.viewLines) == 0 {
374 | 		return vx, vy, nil
375 | 	}
376 | 
377 | 	if vy < len(v.viewLines) {
378 | 		vline := v.viewLines[vy]
379 | 		x = vline.linesX + vx
380 | 		y = vline.linesY
381 | 	} else {
382 | 		vline := v.viewLines[len(v.viewLines)-1]
383 | 		x = vx
384 | 		y = vline.linesY + vy - len(v.viewLines) + 1
385 | 	}
386 | 
387 | 	return x, y, nil
388 | }
389 | 
390 | // Clear empties the view's internal buffer.
391 | func (v *View) Clear() {
392 | 	v.tainted = true
393 | 
394 | 	v.lines = nil
395 | 	v.viewLines = nil
396 | 	v.readOffset = 0
397 | 	v.clearRunes()
398 | }
399 | 
400 | // clearRunes erases all the cells in the view.
401 | func (v *View) clearRunes() {
402 | 	maxX, maxY := v.Size()
403 | 	for x := 0; x < maxX; x++ {
404 | 		for y := 0; y < maxY; y++ {
405 | 			termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ',
406 | 				termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor))
407 | 		}
408 | 	}
409 | }
410 | 
411 | // BufferLines returns the lines in the view's internal
412 | // buffer.
413 | func (v *View) BufferLines() []string {
414 | 	lines := make([]string, len(v.lines))
415 | 	for i, l := range v.lines {
416 | 		str := lineType(l).String()
417 | 		str = strings.Replace(str, "\x00", " ", -1)
418 | 		lines[i] = str
419 | 	}
420 | 	return lines
421 | }
422 | 
423 | // Buffer returns a string with the contents of the view's internal
424 | // buffer.
425 | func (v *View) Buffer() string {
426 | 	str := ""
427 | 	for _, l := range v.lines {
428 | 		str += lineType(l).String() + "\n"
429 | 	}
430 | 	return strings.Replace(str, "\x00", " ", -1)
431 | }
432 | 
433 | // ViewBufferLines returns the lines in the view's internal
434 | // buffer that is shown to the user.
435 | func (v *View) ViewBufferLines() []string {
436 | 	lines := make([]string, len(v.viewLines))
437 | 	for i, l := range v.viewLines {
438 | 		str := lineType(l.line).String()
439 | 		str = strings.Replace(str, "\x00", " ", -1)
440 | 		lines[i] = str
441 | 	}
442 | 	return lines
443 | }
444 | 
445 | // ViewBuffer returns a string with the contents of the view's buffer that is
446 | // shown to the user.
447 | func (v *View) ViewBuffer() string {
448 | 	str := ""
449 | 	for _, l := range v.viewLines {
450 | 		str += lineType(l.line).String() + "\n"
451 | 	}
452 | 	return strings.Replace(str, "\x00", " ", -1)
453 | }
454 | 
455 | // Line returns a string with the line of the view's internal buffer
456 | // at the position corresponding to the point (x, y).
457 | func (v *View) Line(y int) (string, error) {
458 | 	_, y, err := v.realPosition(0, y)
459 | 	if err != nil {
460 | 		return "", err
461 | 	}
462 | 
463 | 	if y < 0 || y >= len(v.lines) {
464 | 		return "", errors.New("invalid point")
465 | 	}
466 | 
467 | 	return lineType(v.lines[y]).String(), nil
468 | }
469 | 
470 | // Word returns a string with the word of the view's internal buffer
471 | // at the position corresponding to the point (x, y).
472 | func (v *View) Word(x, y int) (string, error) {
473 | 	x, y, err := v.realPosition(x, y)
474 | 	if err != nil {
475 | 		return "", err
476 | 	}
477 | 
478 | 	if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
479 | 		return "", errors.New("invalid point")
480 | 	}
481 | 
482 | 	str := lineType(v.lines[y]).String()
483 | 
484 | 	nl := strings.LastIndexFunc(str[:x], indexFunc)
485 | 	if nl == -1 {
486 | 		nl = 0
487 | 	} else {
488 | 		nl = nl + 1
489 | 	}
490 | 	nr := strings.IndexFunc(str[x:], indexFunc)
491 | 	if nr == -1 {
492 | 		nr = len(str)
493 | 	} else {
494 | 		nr = nr + x
495 | 	}
496 | 	return string(str[nl:nr]), nil
497 | }
498 | 
499 | // indexFunc allows to split lines by words taking into account spaces
500 | // and 0.
501 | func indexFunc(r rune) bool {
502 | 	return r == ' ' || r == 0
503 | }
504 | 


--------------------------------------------------------------------------------