mirror of
https://github.com/EnumeratedDev/Typer.git
synced 2025-07-01 07:48:20 +00:00
Add basic dropdown functionality
This commit is contained in:
parent
b52729646c
commit
a2876c2086
@ -68,6 +68,7 @@ func CreateFileBuffer(filename string) (*Buffer, error) {
|
||||
}
|
||||
|
||||
Buffers[buffer.Id] = &buffer
|
||||
LastBufferId++
|
||||
|
||||
return &buffer, nil
|
||||
}
|
||||
@ -82,6 +83,7 @@ func CreateBuffer(bufferName string) *Buffer {
|
||||
}
|
||||
|
||||
Buffers[buffer.Id] = &buffer
|
||||
LastBufferId++
|
||||
|
||||
return &buffer
|
||||
}
|
||||
|
72
src/dropdown.go
Normal file
72
src/dropdown.go
Normal file
@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
type Dropdown struct {
|
||||
Active bool
|
||||
Selected int
|
||||
Options []string
|
||||
PosX, PosY int
|
||||
Width int
|
||||
Action func(int)
|
||||
}
|
||||
|
||||
var dropdowns = make([]*Dropdown, 0)
|
||||
|
||||
func CreateDropdownMenu(options []string, posX, posY, dropdownWidth int, action func(int)) *Dropdown {
|
||||
if len(options) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
width := 0
|
||||
|
||||
if dropdownWidth <= 0 {
|
||||
for _, option := range options {
|
||||
if len(option) > width {
|
||||
width = len(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d := &Dropdown{
|
||||
Active: false,
|
||||
Selected: 0,
|
||||
Options: options,
|
||||
PosX: posX,
|
||||
PosY: posY,
|
||||
Width: width,
|
||||
Action: action,
|
||||
}
|
||||
|
||||
dropdowns = append(dropdowns, d)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func GetActiveDropdown() *Dropdown {
|
||||
for _, dropdown := range dropdowns {
|
||||
if dropdown.Active {
|
||||
return dropdown
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func drawDropdowns(window *Window) {
|
||||
dropdownStyle := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorWhite)
|
||||
for _, d := range dropdowns {
|
||||
drawBox(window.screen, d.PosX, d.PosY, d.PosX+d.Width+1, d.PosY+len(d.Options)+1, dropdownStyle)
|
||||
line := d.PosY
|
||||
for i, option := range d.Options {
|
||||
if d.Selected == i {
|
||||
drawText(window.screen, d.PosX+1, d.PosY+line, d.PosX+d.Width+1, d.PosY+line, dropdownStyle.Background(tcell.Color250), option)
|
||||
} else {
|
||||
drawText(window.screen, d.PosX+1, d.PosY+line, d.PosX+d.Width+1, d.PosY+line, dropdownStyle, option)
|
||||
}
|
||||
|
||||
line++
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ func main() {
|
||||
PrintMessage(window, "Could not open file: "+file)
|
||||
continue
|
||||
}
|
||||
Buffers[b.Id] = b
|
||||
|
||||
if initialBuffer == nil {
|
||||
initialBuffer = b
|
||||
}
|
||||
@ -28,7 +28,6 @@ func main() {
|
||||
}
|
||||
|
||||
if initialBuffer != nil {
|
||||
delete(Buffers, window.textArea.CurrentBuffer.Id)
|
||||
window.textArea.CurrentBuffer = initialBuffer
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell"
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TopMenuButton struct {
|
||||
Name string
|
||||
Name string
|
||||
Key rune
|
||||
Action func(w *Window)
|
||||
}
|
||||
|
||||
var TopMenuButtons = make([]TopMenuButton, 0)
|
||||
@ -14,12 +21,80 @@ func initTopMenu() {
|
||||
// Buttons
|
||||
fileButton := TopMenuButton{
|
||||
Name: "File",
|
||||
Key: 'f',
|
||||
Action: func(window *Window) {
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
d := CreateDropdownMenu([]string{"New", "Save", "Open", "Close", "Quit"}, 0, 1, 0, func(i int) {
|
||||
switch i {
|
||||
case 0:
|
||||
number := 1
|
||||
for _, buffer := range Buffers {
|
||||
if strings.HasPrefix(buffer.Name, "New File ") {
|
||||
number++
|
||||
}
|
||||
}
|
||||
buffer := CreateBuffer(fmt.Sprintf("New File %d", number))
|
||||
window.textArea.CurrentBuffer = buffer
|
||||
window.SetCursorPos(0)
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
delete(Buffers, window.textArea.CurrentBuffer.Id)
|
||||
buffersSlice := slices.Collect(maps.Values(Buffers))
|
||||
if len(buffersSlice) == 0 {
|
||||
window.Close()
|
||||
return
|
||||
}
|
||||
window.textArea.CurrentBuffer = buffersSlice[0]
|
||||
window.SetCursorPos(0)
|
||||
case 4:
|
||||
window.Close()
|
||||
}
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
window.textArea.Typing = true
|
||||
})
|
||||
d.Active = true
|
||||
window.textArea.Typing = false
|
||||
},
|
||||
}
|
||||
EditButton := TopMenuButton{
|
||||
Name: "Edit",
|
||||
Key: 'e',
|
||||
}
|
||||
Buffers := TopMenuButton{
|
||||
Name: "Buffers",
|
||||
Key: 'b',
|
||||
Action: func(window *Window) {
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
buffersSlice := make([]string, 0)
|
||||
for _, buffer := range Buffers {
|
||||
if window.textArea.CurrentBuffer == buffer {
|
||||
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] * %s", buffer.Id, buffer.Name))
|
||||
} else {
|
||||
buffersSlice = append(buffersSlice, fmt.Sprintf("[%d] %s", buffer.Id, buffer.Name))
|
||||
}
|
||||
}
|
||||
|
||||
slices.Sort(buffersSlice)
|
||||
|
||||
d := CreateDropdownMenu(buffersSlice, 0, 1, 0, func(i int) {
|
||||
start := strings.Index(buffersSlice[i], "[")
|
||||
end := strings.Index(buffersSlice[i], "]")
|
||||
|
||||
id, err := strconv.Atoi(buffersSlice[i][start+1 : end])
|
||||
if err != nil {
|
||||
PrintMessage(window, fmt.Sprintf("Cannot convert buffer id '%s' to int", buffersSlice[i][start:end]))
|
||||
return
|
||||
}
|
||||
|
||||
window.textArea.CurrentBuffer = Buffers[id]
|
||||
window.SetCursorPos(0)
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
window.textArea.Typing = true
|
||||
})
|
||||
d.Active = true
|
||||
window.textArea.Typing = false
|
||||
},
|
||||
}
|
||||
|
||||
// Append buttons
|
||||
|
36
src/utils.go
36
src/utils.go
@ -17,3 +17,39 @@ func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style) {
|
||||
if y2 < y1 {
|
||||
y1, y2 = y2, y1
|
||||
}
|
||||
if x2 < x1 {
|
||||
x1, x2 = x2, x1
|
||||
}
|
||||
|
||||
// Fill background
|
||||
for row := y1; row <= y2; row++ {
|
||||
for col := x1; col <= x2; col++ {
|
||||
s.SetContent(col, row, ' ', nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw borders
|
||||
for col := x1; col <= x2; col++ {
|
||||
s.SetContent(col, y1, tcell.RuneHLine, nil, style)
|
||||
s.SetContent(col, y2, tcell.RuneHLine, nil, style)
|
||||
}
|
||||
for row := y1 + 1; row < y2; row++ {
|
||||
s.SetContent(x1, row, tcell.RuneVLine, nil, style)
|
||||
s.SetContent(x2, row, tcell.RuneVLine, nil, style)
|
||||
}
|
||||
|
||||
// Only draw corners if necessary
|
||||
if y1 != y2 && x1 != x2 {
|
||||
s.SetContent(x1, y1, tcell.RuneULCorner, nil, style)
|
||||
s.SetContent(x2, y1, tcell.RuneURCorner, nil, style)
|
||||
s.SetContent(x1, y2, tcell.RuneLLCorner, nil, style)
|
||||
s.SetContent(x2, y2, tcell.RuneLRCorner, nil, style)
|
||||
}
|
||||
|
||||
drawText(s, x1+1, y1+1, x2-1, y2-1, style, " ")
|
||||
}
|
||||
|
146
src/window.go
146
src/window.go
@ -3,6 +3,8 @@ package main
|
||||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
"log"
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type Window struct {
|
||||
@ -16,6 +18,7 @@ type Window struct {
|
||||
|
||||
type TextArea struct {
|
||||
CursorPos int
|
||||
Typing bool
|
||||
CurrentBuffer *Buffer
|
||||
}
|
||||
|
||||
@ -26,6 +29,7 @@ func CreateWindow() (*Window, error) {
|
||||
|
||||
textArea: TextArea{
|
||||
CursorPos: 0,
|
||||
Typing: true,
|
||||
CurrentBuffer: nil,
|
||||
},
|
||||
|
||||
@ -33,7 +37,7 @@ func CreateWindow() (*Window, error) {
|
||||
}
|
||||
|
||||
// Create empty buffer if nil
|
||||
window.textArea.CurrentBuffer = CreateBuffer("New File")
|
||||
window.textArea.CurrentBuffer = CreateBuffer("New File 1")
|
||||
|
||||
// Create tcell screen
|
||||
screen, err := tcell.NewScreen()
|
||||
@ -109,8 +113,15 @@ func (window *Window) Draw() {
|
||||
// Draw message bar
|
||||
drawMessageBar(window)
|
||||
|
||||
// Draw dropdowns
|
||||
drawDropdowns(window)
|
||||
|
||||
// Draw cursor
|
||||
window.screen.ShowCursor(window.GetAbsoluteCursorPos())
|
||||
if window.textArea.Typing {
|
||||
window.screen.ShowCursor(window.GetAbsoluteCursorPos())
|
||||
} else {
|
||||
window.screen.HideCursor()
|
||||
}
|
||||
|
||||
// Update screen
|
||||
window.screen.Show()
|
||||
@ -123,46 +134,93 @@ func (window *Window) Draw() {
|
||||
case *tcell.EventResize:
|
||||
window.screen.Sync()
|
||||
case *tcell.EventKey:
|
||||
// Navigation Keys
|
||||
if ev.Key() == tcell.KeyRight {
|
||||
window.input(ev)
|
||||
}
|
||||
}
|
||||
|
||||
func (window *Window) input(ev *tcell.EventKey) {
|
||||
if ev.Key() == tcell.KeyRight { // Navigation Keys
|
||||
if window.textArea.Typing {
|
||||
window.SetCursorPos(window.textArea.CursorPos + 1)
|
||||
} else if ev.Key() == tcell.KeyLeft {
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyLeft {
|
||||
if window.textArea.Typing {
|
||||
window.SetCursorPos(window.textArea.CursorPos - 1)
|
||||
} else if ev.Key() == tcell.KeyUp {
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyUp {
|
||||
if window.textArea.Typing {
|
||||
x, y := window.GetCursorPos2D()
|
||||
window.SetCursorPos2D(x, y-1)
|
||||
} else if ev.Key() == tcell.KeyDown {
|
||||
} else if GetActiveDropdown() != nil {
|
||||
dropdown := GetActiveDropdown()
|
||||
dropdown.Selected--
|
||||
if dropdown.Selected < 0 {
|
||||
dropdown.Selected = 0
|
||||
}
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyDown {
|
||||
if window.textArea.Typing {
|
||||
x, y := window.GetCursorPos2D()
|
||||
window.SetCursorPos2D(x, y+1)
|
||||
} else if GetActiveDropdown() != nil {
|
||||
dropdown := GetActiveDropdown()
|
||||
dropdown.Selected++
|
||||
if dropdown.Selected >= len(dropdown.Options) {
|
||||
dropdown.Selected = len(dropdown.Options) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// Exit key
|
||||
if ev.Key() == tcell.KeyCtrlC {
|
||||
} else if ev.Key() == tcell.KeyEscape {
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
window.textArea.Typing = true
|
||||
} else if ev.Key() == tcell.KeyCtrlC { // Close buffer key
|
||||
delete(Buffers, window.textArea.CurrentBuffer.Id)
|
||||
buffersSlice := slices.Collect(maps.Values(Buffers))
|
||||
if len(buffersSlice) == 0 {
|
||||
window.Close()
|
||||
return
|
||||
}
|
||||
window.textArea.CurrentBuffer = buffersSlice[0]
|
||||
window.SetCursorPos(0)
|
||||
dropdowns = make([]*Dropdown, 0)
|
||||
window.textArea.Typing = true
|
||||
} else if ev.Key() == tcell.KeyCtrlQ { // Exit key
|
||||
window.Close()
|
||||
} else if ev.Modifiers()&tcell.ModAlt != 0 { // Menu Bar
|
||||
for _, button := range TopMenuButtons {
|
||||
if ev.Rune() == button.Key {
|
||||
button.Action(window)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyBackspace2 { // Typing
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
if index != 0 {
|
||||
str = str[:index-1] + str[index:]
|
||||
window.textArea.CursorPos--
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyTab {
|
||||
if GetActiveDropdown() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Typing
|
||||
if ev.Key() == tcell.KeyBackspace2 {
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
if index != 0 {
|
||||
str = str[:index-1] + str[index:]
|
||||
window.textArea.CursorPos--
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyTab {
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
if index == len(str) {
|
||||
str += "\t"
|
||||
} else {
|
||||
str = str[:index] + "\t" + str[index:]
|
||||
}
|
||||
window.textArea.CursorPos++
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
} else if ev.Key() == tcell.KeyEnter {
|
||||
if index == len(str) {
|
||||
str += "\t"
|
||||
} else {
|
||||
str = str[:index] + "\t" + str[index:]
|
||||
}
|
||||
window.textArea.CursorPos++
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
} else if ev.Key() == tcell.KeyEnter {
|
||||
if GetActiveDropdown() != nil {
|
||||
d := GetActiveDropdown()
|
||||
d.Action(d.Selected)
|
||||
} else {
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
@ -173,18 +231,22 @@ func (window *Window) Draw() {
|
||||
}
|
||||
window.textArea.CursorPos++
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
} else if ev.Key() == tcell.KeyRune {
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
if index == len(str) {
|
||||
str += string(ev.Rune())
|
||||
} else {
|
||||
str = str[:index] + string(ev.Rune()) + str[index:]
|
||||
}
|
||||
window.textArea.CursorPos++
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
}
|
||||
} else if ev.Key() == tcell.KeyRune {
|
||||
if GetActiveDropdown() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
str := window.textArea.CurrentBuffer.Contents
|
||||
index := window.textArea.CursorPos
|
||||
|
||||
if index == len(str) {
|
||||
str += string(ev.Rune())
|
||||
} else {
|
||||
str = str[:index] + string(ev.Rune()) + str[index:]
|
||||
}
|
||||
window.textArea.CursorPos++
|
||||
window.textArea.CurrentBuffer.Contents = str
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user