diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 23e6565..49aa4ea 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -35,6 +35,8 @@ type inputBox struct { textCursor int xScroll int yScroll int + selectionStart int + selectionEnd int } func newInputBox(id string) *inputBox { @@ -173,21 +175,48 @@ func (i *inputBox) handleEnterKey() { } } +func (i *inputBox) selectionOff() { + i.selectionStart = 0 + i.selectionEnd = 0 +} + +func (i *inputBox) selectAll() { + urlInputBox.selectionStart = 0 + urlInputBox.selectionEnd = utf8.RuneCountInString(urlInputBox.text) +} + +func (i *inputBox) removeSelectedText() { + if (i.selectionEnd - i.selectionStart <= 0) { return } + start := i.text[:i.selectionStart] + end := i.text[i.selectionEnd:] + i.text = start + end + i.textCursor = i.selectionStart + i.updateXYCursors() + activeInputBox.selectionOff() +} + func handleInputBoxInput(ev *tcell.EventKey) { switch ev.Key() { case tcell.KeyLeft: + activeInputBox.selectionOff() activeInputBox.cursorLeft() case tcell.KeyRight: + activeInputBox.selectionOff() activeInputBox.cursorRight() case tcell.KeyDown: + activeInputBox.selectionOff() activeInputBox.cursorDown() case tcell.KeyUp: + activeInputBox.selectionOff() activeInputBox.cursorUp() case tcell.KeyBackspace, tcell.KeyBackspace2: + activeInputBox.removeSelectedText() activeInputBox.cursorBackspace() case tcell.KeyEnter: + activeInputBox.removeSelectedText() activeInputBox.handleEnterKey() case tcell.KeyRune: + activeInputBox.removeSelectedText() activeInputBox.cursorInsertRune(ev.Rune()) } if urlInputBox.isActive { diff --git a/interfacer/src/browsh/input_cursor.go b/interfacer/src/browsh/input_cursor.go index f396573..842d237 100644 --- a/interfacer/src/browsh/input_cursor.go +++ b/interfacer/src/browsh/input_cursor.go @@ -3,23 +3,53 @@ package browsh import "unicode/utf8" func (i *inputBox) renderCursor() { - var xCursor int + if i.isSelection() { + i.renderSelectionCursor() + } else { + i.renderSingleCursor() + } +} + +func (i *inputBox) isSelection() bool { + return i.selectionStart > 0 || i.selectionEnd > 0 +} + +func (i *inputBox) renderSingleCursor() { + x, y := i.getCoordsOfCursor() + reverseCellColour(x, y) +} + +func (i *inputBox) renderSelectionCursor() { + var x, y int + textLength := utf8.RuneCountInString(i.text) + for index := 0; index < textLength; index++ { + x, y = i.getCoordsOfIndex(index) + if x >= i.selectionStart && x < i.selectionEnd { + reverseCellColour(x, y) + } + } +} + +func (i *inputBox) getCoordsOfCursor() (int, int) { + var index int + if i.isMultiLine() { + index = i.xCursor + } else { + index = i.textCursor + } + return i.getCoordsOfIndex(index) +} + +func (i *inputBox) getCoordsOfIndex(index int) (int, int) { xFrameOffset := CurrentTab.frame.xScroll yFrameOffset := CurrentTab.frame.yScroll - uiHeight if urlInputBox.isActive { xFrameOffset = 0 yFrameOffset = 0 } - if i.isMultiLine() { - xCursor = i.xCursor - } else { - xCursor = i.textCursor - } - x := (i.X + xCursor) - i.xScroll - xFrameOffset + x := (i.X + index) - i.xScroll - xFrameOffset y := (i.Y + i.yCursor) - i.yScroll - yFrameOffset - mainRune, combiningRunes, style, _ := screen.GetContent(x, y) - style = style.Reverse(true) - screen.SetContent(x, y, mainRune, combiningRunes, style) + return x, y } func (i *inputBox) cursorLeft() { diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 3237537..594c076 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -91,12 +91,14 @@ func urlBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false + urlInputBox.selectionOff() } else { activeInputBox = &urlInputBox urlInputBox.isActive = true urlInputBox.xScroll = 0 urlInputBox.text = CurrentTab.URI urlInputBox.putCursorAtEnd() + urlInputBox.selectAll() } } @@ -105,3 +107,8 @@ func overlayPageStatusMessage() { writeString(0, height - 1, CurrentTab.StatusMessage, tcell.StyleDefault) } +func reverseCellColour(x, y int) { + mainRune, combiningRunes, style, _ := screen.GetContent(x, y) + style = style.Reverse(true) + screen.SetContent(x, y, mainRune, combiningRunes, style) +} diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 3d0a9f2..196d988 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -107,7 +107,6 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { SpecialKey(tcell.KeyCtrlL) - BackspaceRemoveURL() Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() @@ -116,16 +115,6 @@ func GotoURL(url string) { gomega.Expect(url).To(BeInFrameAt(0, 1)) } -// BackspaceRemoveURL holds down the backspace key to delete the existing URL -// TODO: Remove when text input supports selecting all and pressing any key to overwrite -// the selection. -func BackspaceRemoveURL() { - for i := 1; i <= 50; i++ { - simScreen.InjectKey(tcell.KeyBackspace2, 0, tcell.ModNone) - time.Sleep(10 * time.Millisecond) - } -} - func mouseClick(x, y int) { simScreen.InjectMouse(x, y, 1, tcell.ModNone) simScreen.InjectMouse(x, y, 0, tcell.ModNone) diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 0c99d87..780b56d 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -28,7 +28,6 @@ var _ = Describe("Showing a basic webpage", func() { Describe("Interaction", func() { It("should navigate to a new page by using the URL bar", func() { SpecialKey(tcell.KeyCtrlL) - BackspaceRemoveURL() Keyboard(testSiteURL + "/smorgasbord/another.html") SpecialKey(tcell.KeyEnter) Expect("Another").To(BeInFrameAt(0, 0))