mirror of https://github.com/liamg/aminal.git
Merge pull request #40 from liamg/url-detection
added url detection and clicking
This commit is contained in:
commit
28735e53db
|
@ -64,6 +64,7 @@ Ensure you have your latest graphics card drivers installed before use.
|
|||
| Config file | ✔ |
|
||||
| Scrolling | ✔ |
|
||||
| Mouse interaction | ✔ |
|
||||
| Clickable URLs | ✔ |
|
||||
| Sweet render effects | |
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
|
|
@ -2,6 +2,7 @@ package buffer
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -47,6 +48,45 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer {
|
|||
return b
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetURLAtPosition(col uint16, row uint16) string {
|
||||
|
||||
cell := buffer.GetCell(col, row)
|
||||
if cell == nil || cell.Rune() == 0x00 {
|
||||
return ""
|
||||
}
|
||||
|
||||
candidate := ""
|
||||
|
||||
for i := col; i >= 0; i-- {
|
||||
cell := buffer.GetCell(i, row)
|
||||
if cell == nil {
|
||||
break
|
||||
}
|
||||
if isRuneURLSelectionMarker(cell.Rune()) {
|
||||
break
|
||||
}
|
||||
candidate = fmt.Sprintf("%c%s", cell.Rune(), candidate)
|
||||
}
|
||||
|
||||
for i := col + 1; i < buffer.viewWidth; i++ {
|
||||
cell := buffer.GetCell(i, row)
|
||||
if cell == nil {
|
||||
break
|
||||
}
|
||||
if isRuneURLSelectionMarker(cell.Rune()) {
|
||||
break
|
||||
}
|
||||
candidate = fmt.Sprintf("%s%c", candidate, cell.Rune())
|
||||
}
|
||||
|
||||
// check if url
|
||||
_, err := url.ParseRequestURI(candidate)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) {
|
||||
|
||||
cell := buffer.GetCell(col, row)
|
||||
|
@ -62,7 +102,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) {
|
|||
if cell == nil {
|
||||
break
|
||||
}
|
||||
if isRuneSelectionMarker(cell.Rune()) {
|
||||
if isRuneWordSelectionMarker(cell.Rune()) {
|
||||
break
|
||||
}
|
||||
start = i
|
||||
|
@ -73,7 +113,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) {
|
|||
if cell == nil {
|
||||
break
|
||||
}
|
||||
if isRuneSelectionMarker(cell.Rune()) {
|
||||
if isRuneWordSelectionMarker(cell.Rune()) {
|
||||
break
|
||||
}
|
||||
end = i
|
||||
|
@ -92,7 +132,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) {
|
|||
}
|
||||
|
||||
// bounds for word selection
|
||||
func isRuneSelectionMarker(r rune) bool {
|
||||
func isRuneWordSelectionMarker(r rune) bool {
|
||||
switch r {
|
||||
case ',', ' ', ':', ';', 0, '\'', '"', '[', ']', '(', ')', '{', '}':
|
||||
return true
|
||||
|
@ -101,6 +141,15 @@ func isRuneSelectionMarker(r rune) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func isRuneURLSelectionMarker(r rune) bool {
|
||||
switch r {
|
||||
case ' ', 0, '\'', '"', '{', '}':
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetSelectedText() string {
|
||||
if buffer.selectionStart == nil || buffer.selectionEnd == nil {
|
||||
return ""
|
||||
|
|
33
gui/mouse.go
33
gui/mouse.go
|
@ -3,6 +3,8 @@ package gui
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
"github.com/liamg/aminal/terminal"
|
||||
|
@ -10,12 +12,34 @@ import (
|
|||
|
||||
func (gui *GUI) mouseMoveCallback(w *glfw.Window, xpos float64, ypos float64) {
|
||||
|
||||
px, py := w.GetCursorPos()
|
||||
x := uint16(math.Floor((px - float64(gui.renderer.areaX)) / float64(gui.renderer.CellWidth())))
|
||||
y := uint16(math.Floor((py - float64(gui.renderer.areaY)) / float64(gui.renderer.CellHeight())))
|
||||
if gui.mouseDown {
|
||||
px, py := w.GetCursorPos()
|
||||
x := uint16(math.Floor((px - float64(gui.renderer.areaX)) / float64(gui.renderer.CellWidth())))
|
||||
y := uint16(math.Floor((py - float64(gui.renderer.areaY)) / float64(gui.renderer.CellHeight())))
|
||||
gui.terminal.ActiveBuffer().EndSelection(x, y, false)
|
||||
}
|
||||
|
||||
if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" {
|
||||
w.SetCursor(glfw.CreateStandardCursor(glfw.HandCursor))
|
||||
} else {
|
||||
w.SetCursor(glfw.CreateStandardCursor(glfw.ArrowCursor))
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *GUI) launchTarget(target string) {
|
||||
|
||||
cmd := "xdg-open"
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmd = "open"
|
||||
case "windows":
|
||||
cmd = "start"
|
||||
}
|
||||
|
||||
if err := exec.Command(cmd, target).Run(); err != nil {
|
||||
gui.logger.Errorf("Failed to launch external command %s: %s", cmd, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *GUI) mouseButtonCallback(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
|
||||
|
@ -33,6 +57,9 @@ func (gui *GUI) mouseButtonCallback(w *glfw.Window, button glfw.MouseButton, act
|
|||
} else if action == glfw.Release {
|
||||
gui.mouseDown = false
|
||||
gui.terminal.ActiveBuffer().EndSelection(x, y, true)
|
||||
if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" {
|
||||
go gui.launchTarget(url)
|
||||
}
|
||||
}
|
||||
// https://www.xfree86.org/4.8.0/ctlseqs.html
|
||||
|
||||
|
|
Loading…
Reference in New Issue