From ed8cd6d64f2c0b63bcd0d86cd1e07e5c2f42e762 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Wed, 20 Nov 2019 04:48:44 -0800 Subject: [PATCH] Add tview-and-you --- config/_default/config.toml | 2 +- config/_default/menus.toml | 11 +- config/_default/params.toml | 2 +- content/authors/admin/_index.md | 7 +- content/home/posts.md | 2 +- ...ha-slice-append-backing-array-overwrite.md | 28 +- content/post/tview-and-you.md | 321 ++++++++++++++++++ content/project/netris.md | 8 + 8 files changed, 368 insertions(+), 13 deletions(-) create mode 100644 content/post/tview-and-you.md create mode 100644 content/project/netris.md diff --git a/config/_default/config.toml b/config/_default/config.toml index 071b65c..60800f8 100644 --- a/config/_default/config.toml +++ b/config/_default/config.toml @@ -16,7 +16,7 @@ baseurl = "https://rocketnine.space/" copyright = "" # Enable analytics by entering your Google Analytics tracking ID -googleAnalytics = "UA-71983-61" +googleAnalytics = "" ############################ ## Advanced options below ## diff --git a/config/_default/menus.toml b/config/_default/menus.toml index 28917c4..298a094 100644 --- a/config/_default/menus.toml +++ b/config/_default/menus.toml @@ -8,10 +8,10 @@ url = "#about" weight = 10 -#[[main]] -# name = "Posts" -# url = "#posts" -# weight = 20 +[[main]] + name = "Posts" + url = "#posts" + weight = 20 [[main]] name = "Projects" @@ -34,3 +34,6 @@ # name = "CV" # url = "files/cv.pdf" # weight = 70 + +[[post]] + diff --git a/config/_default/params.toml b/config/_default/params.toml index 3573741..ce45446 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -36,7 +36,7 @@ logo = "" # Enable source code highlighting? true/false # Documentation: https://sourcethemes.com/academic/docs/writing-markdown-latex/#highlighting-options highlight = true -highlight_languages = ["r"] # Add support for highlighting additional languages +highlight_languages = ["r", "go", "golang"] # Add support for highlighting additional languages # highlight_style = "github" # For supported styles, see https://cdnjs.com/libraries/highlight.js/ # Enable LaTeX math rendering? true/false diff --git a/content/authors/admin/_index.md b/content/authors/admin/_index.md index b53ada4..2226c49 100644 --- a/content/authors/admin/_index.md +++ b/content/authors/admin/_index.md @@ -18,7 +18,7 @@ role: Developer &
System Administrator # url: "" # Short bio (displayed in user profile at end of posts) -bio: Install Gentoo +bio: #interests: #- Open source software @@ -72,7 +72,6 @@ email: "trevor@rocketnine.space" I use [Linux](https://en.wikipedia.org/wiki/Linux) to write [open-source software](https://en.wikipedia.org/wiki/Open-source_software). -My projects are available on [sourcehut](https://sr.ht) and [GitLab](https://gitlab.com/users/tslocum/projects). - -Be sure to check out [stick](https://git.sr.ht/~tslocum/stick) and [gophast](https://git.sr.ht/~tslocum/gophast). +My projects are available on [sourcehut](https://git.sr.ht/~tslocum) and [GitLab](https://gitlab.com/users/tslocum/projects). +Be sure to check out [stick](https://git.sr.ht/~tslocum/stick) and [netris](https://git.sr.ht/~tslocum/netris). diff --git a/content/home/posts.md b/content/home/posts.md index 4afc303..12b3b6f 100644 --- a/content/home/posts.md +++ b/content/home/posts.md @@ -4,7 +4,7 @@ widget = "pages" # See https://sourcethemes.com/academic/docs/page-builder/ headless = true # This file represents a page section. -active = false # Activate this widget? true/false +active = true # Activate this widget? true/false weight = 60 # Order that this section will appear. title = "Recent Posts" diff --git a/content/post/gotcha-slice-append-backing-array-overwrite.md b/content/post/gotcha-slice-append-backing-array-overwrite.md index cefd81e..cc4ddaa 100644 --- a/content/post/gotcha-slice-append-backing-array-overwrite.md +++ b/content/post/gotcha-slice-append-backing-array-overwrite.md @@ -1,7 +1,31 @@ --- -title: "Gotcha - Overiting a shared backing array when calling append" +title: "Gotcha - Up-ending a backing array" date: 2019-09-26T21:42:18-07:00 categories: [gotcha] +draft: true --- -Test +To give better context to the problem being presented in this article, we must go over how slices work. + +A slice value consists of three parts: a pointer to a backing array, a length and a capacity. + +Excerpt of **runtime/slice.go** from the standard library: + +```go + +type slice struct { + array unsafe.Pointer + len int + cap int +} +``` + +When calling append on a slice whose length is the same as its capacity, a new backing array of greater size is created. The previous backing array's contents are copied. + +A range of elements from a slice may be selected like so: + +```go +bar := foo[4:7] +``` + +When calling append on such a slice, diff --git a/content/post/tview-and-you.md b/content/post/tview-and-you.md new file mode 100644 index 0000000..1461bb0 --- /dev/null +++ b/content/post/tview-and-you.md @@ -0,0 +1,321 @@ +--- +title: "tview and you - Creating Rich Terminal User Interfaces" +date: 2019-11-08T01:42:18-07:00 +categories: [tutorial] +--- + + + +This is an introduction to using [tview](https://github.com/rivo/tview) to create rich terminal-based user interfaces with Go. + +## Contents + +* [Primitives](#primitives) +* [Widgets](#widgets) + * [Elements](#widget-elements) + * [Containers](#widget-containers) +* [Thread Safety](#thread-safety) +* [Example Application](#example-application) + +# Primitives + +The [Primitive](https://godoc.org/github.com/rivo/tview#Primitive) interface is as follows: + +```go +type Primitive interface { + Draw(screen tcell.Screen) + GetRect() (int, int, int, int) + SetRect(x, y, width, height int) + InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) + Focus(delegate func(p Primitive)) + Blur() + GetFocusable() Focusable +} +``` + +[Box](https://godoc.org/github.com/rivo/tview#Box) is the only primitive implemented. +It has a size, padding amount, optional border, optional title and background color. + +# Widgets + +Widgets are structs which embed a [Box](https://godoc.org/github.com/rivo/tview#Box) and build upon it. + +From the [TextView](https://godoc.org/github.com/rivo/tview#TextView) declaration: + +```go +type TextView struct { + *Box + + // The text buffer. + buffer []string + + // The text alignment, one of AlignLeft, AlignCenter, or AlignRight. + align int + + // ... +} +``` + +Some widgets allow nesting other widgets within them, such as [Grid](https://godoc.org/github.com/rivo/tview#Grid). + +Most widget commands may be chained together: + +```go +nameLabel := tview.NewTextView(). + SetTextAlign(tview.AlignRight). + SetDynamicColors(true). + SetWrap(true). + SetWordWrap(true). + SetText("Please enter your name:") +``` + +New widgets may be defined, as documented in the [Primitive demo](https://github.com/rivo/tview/tree/master/demos/primitive). + +## Widget Elements + +--- + +[**Button**](https://godoc.org/github.com/rivo/tview#Button) is a labeled box that triggers an action when selected. + +```go +button := tview.NewButton("OK").SetSelectedFunc(func() { + pressedOK() +}) +``` + +--- + +[**Checkbox**](https://godoc.org/github.com/rivo/tview#Checkbox) holds a label and boolean value which may be checked and unchecked. + +```go +checkbox := tview.NewCheckbox().SetLabel("Toggle value with Enter: ") +``` + +--- + +[**DropDown**](https://godoc.org/github.com/rivo/tview#DropDown) holds one or more options which may be selected as a dropdown list. + +```go +dropdown := tview.NewDropDown(). + SetLabel("Select an option with Enter: "). + SetOptions([]string{"Foo", "Bar", "Baz"}, nil) +``` + +--- + +[**InputField**](https://godoc.org/github.com/rivo/tview#InputField) is a box where text may be entered. + +```go +inputField := tview.NewInputField(). + SetLabel("Name: "). + SetPlaceholder("John Smith"). + SetFieldWidth(14). + SetDoneFunc(func(key tcell.Key) { + processName() + }) +``` + +--- + +[**Modal**](https://godoc.org/github.com/rivo/tview#Modal) is a centered message window which may have one or more buttons. + +```go +modal := tview.NewModal(). + SetText("Are you sure you want to exit?"). + AddButtons([]string{"Cancel", "Quit"}). + SetDoneFunc(func(buttonIndex int, buttonLabel string) { + if buttonIndex == 1 { + app.Stop() + } + }) +``` + +--- + +[**TextView**](https://godoc.org/github.com/rivo/tview#TextView) is a box containing text. +[Colored text](https://godoc.org/github.com/rivo/tview#hdr-Colors) is supported when enabled. + +```go +textView := tview.NewTextView(). + SetWrap(true). + SetWordWrap(true). + SetText("Hello, World!") +``` + +--- + +## Widget Containers + +--- + +[**Flex**](https://godoc.org/github.com/rivo/tview#Flex) is a [flexbox layout](https://en.wikipedia.org/wiki/CSS_Flexible_Box_Layout) container. + +See the [Flex demo](https://github.com/rivo/tview/tree/master/demos/flex) for example usage. + +--- + +[**Grid**](https://godoc.org/github.com/rivo/tview#Grid) is a [grid layout](https://en.wikipedia.org/wiki/CSS_grid_layout) container. + +See the [Grid demo](https://github.com/rivo/tview/tree/master/demos/grid) for example usage. + +--- + +[**Form**](https://godoc.org/github.com/rivo/tview#Form) displays one or more form elements in a vertical or horizontal layout. + +See the [Form demo](https://github.com/rivo/tview/tree/master/demos/form) for example usage. + +--- + +[**List**](https://godoc.org/github.com/rivo/tview#List) displays one or more widgets as a selectable list. + +See the [List demo](https://github.com/rivo/tview/tree/master/demos/list) for example usage. + +--- + +[**Pages**](https://godoc.org/github.com/rivo/tview#Pages) displays one or more widgets at a time. + +See the [Pages demo](https://github.com/rivo/tview/tree/master/demos/pages) for example usage. + +--- + +[**Table**](https://godoc.org/github.com/rivo/tview#Table) displays one or more widgets in rows and columns. + +See the [Table demo](https://github.com/rivo/tview/tree/master/demos/table) for example usage. + +--- + +[**TreeView**](https://godoc.org/github.com/rivo/tview#TreeView) displays one or more widgets in a tree. + +See the [TreeView demo](https://github.com/rivo/tview/tree/master/demos/treeview) for example usage. + +--- + +# Thread Safety + +**Most tview functions cannot safely be called from any thread except the main one**. + +Using either of the following functions, we can queue a function to be executed in the main thread. + +* [Application.QueueUpdate](https://godoc.org/github.com/rivo/tview#Application.QueueUpdate) +* [Application.QueueUpdateDraw](https://godoc.org/github.com/rivo/tview#Application.QueueUpdateDraw) + +The only difference between the two is that QueueUpdateDraw calls [Application.Draw](https://godoc.org/github.com/rivo/tview#Application.Draw) after the queued function returns. + +One exception is [TextView.Write](https://godoc.org/github.com/rivo/tview#TextView.Write), which may safely be called from multiple goroutines. + +Below is an example of setting a new root primitive from another goroutine. + +```go +app.QueueUpdateDraw(func() { + app.SetRoot(appGrid, true) +}) +``` + +# Example Application + +A tview application is constructed of a running [Application](https://godoc.org/github.com/rivo/tview#Application) with at least one root widget. + +To display a primitive (and its contents), we call [Application.SetRoot](https://godoc.org/github.com/rivo/tview#Application.SetRoot). + +This function has two arguments, a primitive which will become the root of the screen, and a boolean which controls whether the primitive will be resized to fit the screen. + +In this example, the root is a Grid containing a label, an input and a submit button. For a more complex example, see [netris](https://git.sr.ht/~tslocum/netris). + +Install tview if you haven't already: + +```command +go get -u github.com/rivo/tview +``` + +Then create a file named greet.go: + +```go +package main + +import ( + "fmt" + "log" + "strings" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" +) + +func main() { + // Initialize application + app := tview.NewApplication() + + // Create label + label := tview.NewTextView().SetText("Please enter your name:") + + // Create input field + input := tview.NewInputField() + + // Create submit button + btn := tview.NewButton("Submit") + + // Create empty Box to pad each side of appGrid + bx := tview.NewBox() + + // Create Grid containing the application's widgets + appGrid := tview.NewGrid(). + SetColumns(-1, 24, 16, -1). + SetRows(-1, 2, 3, -1). + AddItem(bx, 0, 0, 3, 1, 0, 0, false). // Left - 3 rows + AddItem(bx, 0, 1, 1, 1, 0, 0, false). // Top - 1 row + AddItem(bx, 0, 3, 3, 1, 0, 0, false). // Right - 3 rows + AddItem(bx, 3, 1, 1, 1, 0, 0, false). // Bottom - 1 row + AddItem(label, 1, 1, 1, 1, 0, 0, false). + AddItem(input, 1, 2, 1, 1, 0, 0, false). + AddItem(btn, 2, 1, 1, 2, 0, 0, false) + + // submittedName is toggled each time Enter is pressed + var submittedName bool + + // Capture user input + app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + // Anything handled here will be executed on the main thread + switch event.Key() { + case tcell.KeyEnter: + submittedName = !submittedName + + if submittedName { + name := input.GetText() + if strings.TrimSpace(name) == "" { + name = "Anonymous" + } + + // Create a modal dialog + m := tview.NewModal(). + SetText(fmt.Sprintf("Greetings, %s!", name)). + AddButtons([]string{"Hello"}) + + // Display and focus the dialog + app.SetRoot(m, true).SetFocus(m) + } else { + // Clear the input field + input.SetText("") + + // Display appGrid and focus the input field + app.SetRoot(appGrid, true).SetFocus(input) + } + return nil + case tcell.KeyEsc: + // Exit the application + app.Stop() + return nil + } + + return event + }) + + // Set the grid as the application root and focus the input field + app.SetRoot(appGrid, true).SetFocus(input) + + // Run the application + err := app.Run() + if err != nil { + log.Fatal(err) + } +} +``` diff --git a/content/project/netris.md b/content/project/netris.md new file mode 100644 index 0000000..d194f0a --- /dev/null +++ b/content/project/netris.md @@ -0,0 +1,8 @@ +--- +title: netris +summary: Multiplayer Tetris clone +tags: +- Go + +external_link: "https://git.sr.ht/~tslocum/netris" +---