Skip to content

Commit

Permalink
Tabs! As we all know and love
Browse files Browse the repository at this point in the history
Adding, cycling, deleting. The width of the tab handle is currently
fixed to 20. And if there are more tabs than can fit in the tab bar then
the extra ones just dissapear off to the right, but they can still be
cycled to with CTRL-Tab.

The marks the end of feature development in preperation for a version
1 release.
  • Loading branch information
tombh committed May 24, 2018
1 parent 5c7ff71 commit 938d51b
Show file tree
Hide file tree
Showing 15 changed files with 566 additions and 285 deletions.
8 changes: 4 additions & 4 deletions interfacer/src/browsh/browsh.go
Expand Up @@ -142,21 +142,21 @@ func Shell(command string) string {
return stripWhitespace(string(out))
}

// Start ... Start Browsh
// Start starts Browsh
func Start(injectedScreen tcell.Screen) {
var isTesting = fmt.Sprintf("%T", injectedScreen) == "*tcell.simscreen"
screen = injectedScreen
initialise(isTesting)
if !*isUseExistingFirefox {
if isTesting {
writeString(0, 0, "Starting Browsh in test mode...")
writeString(0, 0, "Starting Browsh in test mode...", tcell.StyleDefault)
go startWERFirefox()
} else {
writeString(0, 0, "Starting Browsh, the modern terminal web browser...")
writeString(0, 0, "Starting Browsh, the modern terminal web browser...", tcell.StyleDefault)
setupFirefox()
}
} else {
writeString(0, 0, "Waiting for a Firefox instance to connect...")
writeString(0, 0, "Waiting for a Firefox instance to connect...", tcell.StyleDefault)
}
Log("Starting Browsh CLI client")
go readStdin()
Expand Down
2 changes: 1 addition & 1 deletion interfacer/src/browsh/firefox.go
Expand Up @@ -93,7 +93,7 @@ func startWERFirefox() {
"--firefox=" + rootDir + "/webext/contrib/firefoxheadless.sh",
"--verbose",
"--no-reload",
"--url=http://localhost:" + TestServerPort + "/smorgasbord",
"--url=https://www.google.com",
}
firefoxProcess := exec.Command(rootDir+"/webext/node_modules/.bin/web-ext", args...)
firefoxProcess.Dir = rootDir + "/webext/dist/"
Expand Down
13 changes: 9 additions & 4 deletions interfacer/src/browsh/frame_builder.go
@@ -1,6 +1,7 @@
package browsh

import (
"fmt"
"encoding/json"
"unicode"

Expand Down Expand Up @@ -76,14 +77,16 @@ func parseJSONFrameText(jsonString string) {
if err := json.Unmarshal(jsonBytes, &incoming); err != nil {
Shutdown(err)
}
ensureTabExists(incoming.Meta.TabID)
if (!isTabPresent(incoming.Meta.TabID)) {
Log(fmt.Sprintf("Not building frame for non-existent tab ID: %d", incoming.Meta.TabID))
return
}
tabs[incoming.Meta.TabID].frame.buildFrameText(incoming)
}

func (f *frame) buildFrameText(incoming incomingFrameText) {
f.setup(incoming.Meta)
if (!f.isIncomingFrameTextValid(incoming)) { return }
CurrentTab = tabs[incoming.Meta.TabID]
f.updateInputBoxes(incoming)
f.populateFrameText(incoming)
}
Expand All @@ -94,15 +97,17 @@ func parseJSONFramePixels(jsonString string) {
if err := json.Unmarshal(jsonBytes, &incoming); err != nil {
Shutdown(err)
}
ensureTabExists(incoming.Meta.TabID)
if (!isTabPresent(incoming.Meta.TabID)) {
Log(fmt.Sprintf("Not building frame for non-existent tab ID: %d", incoming.Meta.TabID))
return
}
if (len(tabs[incoming.Meta.TabID].frame.text) == 0) { return }
tabs[incoming.Meta.TabID].frame.buildFramePixels(incoming)
}

func (f *frame) buildFramePixels(incoming incomingFramePixels) {
f.setup(incoming.Meta)
if (!f.isIncomingFramePixelsValid(incoming)) { return }
CurrentTab = tabs[incoming.Meta.TabID]
f.populateFramePixels(incoming)
}

Expand Down
6 changes: 5 additions & 1 deletion interfacer/src/browsh/input_box.go
Expand Up @@ -168,7 +168,11 @@ func handleInputBoxInput(ev *tcell.EventKey) {
activeInputBox.cursorBackspace()
case tcell.KeyEnter:
if urlInputBox.isActive {
sendMessageToWebExtension("/url_bar," + activeInputBox.text)
if isNewEmptyTabActive() {
sendMessageToWebExtension("/new_tab," + activeInputBox.text)
} else {
sendMessageToWebExtension("/url_bar," + activeInputBox.text)
}
urlBarFocus(false)
}
case tcell.KeyRune:
Expand Down
109 changes: 102 additions & 7 deletions interfacer/src/browsh/tab.go
@@ -1,16 +1,24 @@
package browsh

import (
"fmt"
"encoding/json"
)

// Map of all tab data
var tabs = make(map[int]*tab)
// Slice of the order in which tabs appear in the tab bar
var tabsOrder []int
// There can be a race condition between the webext sending a tab state update and the
// the tab being deleted, so we need to keep track of all deleted IDs
var tabsDeleted []int
// CurrentTab is the currently active tab in the TTY browser
var CurrentTab *tab

// A single tab synced from the browser
type tab struct {
ID int `json:"id"`
Active bool `json:"active"`
Title string `json:"title"`
URI string `json:"uri"`
PageState string `json:"page_state"`
Expand All @@ -20,24 +28,111 @@ type tab struct {

func ensureTabExists(id int) {
if _, ok := tabs[id]; !ok {
tabs[id] = &tab{
ID: id,
frame: frame{
xScroll: 0,
yScroll: 0,
},
newTab(id)
if isNewEmptyTabActive() {
removeTab(-1)
}
}
}

func isTabPresent(id int) bool {
_, ok := tabs[id]
return ok
}

func newTab(id int) {
tabsOrder = append(tabsOrder, id)
tabs[id] = &tab{
ID: id,
frame: frame{
xScroll: 0,
yScroll: 0,
},
}
}

func removeTab(id int) {
if (len(tabs) == 1) { quitBrowsh() }
tabsDeleted = append(tabsDeleted, id)
sendMessageToWebExtension(fmt.Sprintf("/remove_tab,%d", id))
nextTab()
removeTabIDfromTabsOrder(id)
delete(tabs, id)
renderUI()
renderCurrentTabWindow()
}

// A bit complicated! Just want to remove an integer from a slice whilst retaining
// order :/
func removeTabIDfromTabsOrder(id int) {
for i := 0; i < len(tabsOrder); i++ {
if tabsOrder[i] == id {
tabsOrder = append(tabsOrder[:i], tabsOrder[i+1:]...)
}
}
}

// Creating a new tab in the browser without a URI means it won't register with the
// web extension, which means that, come the moment when we actually have a URI for the new
// tab then we can't talk to it to tell it navigate. So we need to only create a real new
// tab when we actually have a URL.
func createNewEmptyTab() {
if isNewEmptyTabActive() { return }
newTab(-1)
tab := tabs[-1]
tab.Title = "New Tab"
tab.URI = ""
tab.Active = true
CurrentTab = tab
CurrentTab.frame.resetCells()
renderUI()
urlBarFocus(true)
renderCurrentTabWindow()
}

func isNewEmptyTabActive() bool {
return isTabPresent(-1)
}

func nextTab() {
for i := 0; i < len(tabsOrder); i++ {
if tabsOrder[i] == CurrentTab.ID {
if (i + 1 == len(tabsOrder)) {
i = 0;
} else {
i++
}
sendMessageToWebExtension(fmt.Sprintf("/switch_to_tab,%d", tabsOrder[i]))
CurrentTab = tabs[tabsOrder[i]]
renderUI()
renderCurrentTabWindow()
break
}
}
}

func isTabPreviouslyDeleted(id int) bool {
for i := 0; i < len(tabsDeleted); i++ {
if tabsDeleted[i] == id {
return true
}
}
return false
}

func parseJSONTabState(jsonString string) {
var incoming tab
jsonBytes := []byte(jsonString)
if err := json.Unmarshal(jsonBytes, &incoming); err != nil {
Shutdown(err)
}
if (isTabPreviouslyDeleted(incoming.ID)) {
return
}
ensureTabExists(incoming.ID)
CurrentTab = tabs[incoming.ID]
if (incoming.Active && !isNewEmptyTabActive()) {
CurrentTab = tabs[incoming.ID]
}
tabs[incoming.ID].handleStateChange(&incoming)
}

Expand Down
10 changes: 8 additions & 2 deletions interfacer/src/browsh/tty.go
Expand Up @@ -3,7 +3,6 @@ package browsh
import (
"fmt"
"os"
"unicode/utf8"
"encoding/json"

"github.com/gdamore/tcell"
Expand Down Expand Up @@ -54,10 +53,17 @@ func handleUserKeyPress(ev *tcell.EventKey) {
quitBrowsh()
case tcell.KeyCtrlL:
urlBarFocusToggle()
case tcell.KeyCtrlT:
createNewEmptyTab()
case tcell.KeyCtrlW:
removeTab(CurrentTab.ID)
}
if (ev.Rune() == 'm' && ev.Modifiers() == 4) {
toggleMonochromeMode()
}
if (ev.Key() == 9 && ev.Modifiers() == 0) {
nextTab()
}
forwardKeyPress(ev)
if activeInputBox != nil {
handleInputBoxInput(ev)
Expand Down Expand Up @@ -135,7 +141,7 @@ func handleMouseEvent(ev *tcell.EventMouse) {
func handleTTYResize() {
width, _ := screen.Size()
// TODO: How does this work with wide UTF8 chars?
urlInputBox.Width = width - utf8.RuneCountInString(urlBarControls)
urlInputBox.Width = width
screen.Sync()
sendTtySize()
}
Expand Down
38 changes: 20 additions & 18 deletions interfacer/src/browsh/ui.go
Expand Up @@ -7,9 +7,8 @@ import (
)

var (
urlBarControls = " ← | x | "
urlInputBox = inputBox{
X: utf8.RuneCountInString(urlBarControls),
X: 0,
Y: 1,
Height: 1,
text: "",
Expand All @@ -27,12 +26,9 @@ func renderUI() {
// Write a simple text string to the screen.
// Not for use in the browser frames themselves. If you want anything to appear in
// the browser that must be done through the webextension.
func writeString(x, y int, str string) {
var defaultColours = tcell.StyleDefault
rgb := tcell.NewHexColor(int32(0xffffff))
defaultColours.Foreground(rgb)
func writeString(x, y int, str string, style tcell.Style) {
for _, c := range str {
screen.SetContent(x, y, c, nil, defaultColours)
screen.SetContent(x, y, c, nil, style)
x++
}
screen.Show()
Expand All @@ -41,34 +37,40 @@ func writeString(x, y int, str string) {
func fillLineToEnd(x, y int) {
width, _ := screen.Size()
for i := x; i < width - 1; i++ {
writeString(i, y, " ")
writeString(i, y, " ", tcell.StyleDefault)
}
}

func renderTabs() {
var tab *tab
var style tcell.Style
count := 0
xPosition := 0
tabTitleLength := 15
for _, tab := range tabs {
if (tab.frame.text == nil) { continue } // TODO: this shouldn't be needed
tabTitleLength := 20
for _, tabID := range tabsOrder {
tab = tabs[tabID]
tabTitle := []rune(tab.Title)
tabTitleContent := string(tabTitle[0:tabTitleLength]) + " |x "
writeString(xPosition, 0, tabTitleContent)
tabTitleContent := string(tabTitle[0:tabTitleLength])
style = tcell.StyleDefault
if (CurrentTab.ID == tabID) { style = tcell.StyleDefault.Reverse(true) }
writeString(xPosition, 0, tabTitleContent, style)
style = tcell.StyleDefault.Reverse(false)
count++
xPosition = (count * tabTitleLength) + 4
xPosition = count * (tabTitleLength + 1)
writeString(xPosition - 1, 0, "|", style)
}
fillLineToEnd(xPosition, 0)
}

func renderURLBar() {
content := urlBarControls
var content string
if urlInputBox.isActive {
writeString(0, 1, content)
writeString(0, 1, content, tcell.StyleDefault)
content += urlInputBox.text + " "
urlInputBox.renderURLBox()
} else {
content += CurrentTab.URI
writeString(0, 1, content)
writeString(0, 1, content, tcell.StyleDefault)
}
fillLineToEnd(utf8.RuneCountInString(content), 1)
}
Expand All @@ -95,6 +97,6 @@ func urlBarFocus(on bool) {

func overlayPageStatusMessage() {
_, height := screen.Size()
writeString(0, height - 1, CurrentTab.StatusMessage)
writeString(0, height - 1, CurrentTab.StatusMessage, tcell.StyleDefault)
}

0 comments on commit 938d51b

Please sign in to comment.