Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
yorukot committed Apr 27, 2024
2 parents 3982697 + e21a387 commit 53d5bcc
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 35 deletions.
5 changes: 5 additions & 0 deletions src/components/default_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ func getHelpMenuData() []helpMenuModalData {
description: "Quit",
hotkeyWorkType: globalType,
},
{
hotkey: hotkeys.OpenHelpMenu,
description: "Open help menu(hotkeylist)",
hotkeyWorkType: globalType,
},
{
subTitle: "Panel navigation",
},
Expand Down
61 changes: 38 additions & 23 deletions src/components/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ func InitialModel(dir string) model {
},
helpMenu: helpMenuModal{
renderIndex: 0,
cursor: 1,
data: getHelpMenuData(),
open: false,
cursor: 1,
data: getHelpMenuData(),
open: false,
},
toggleDotFile: toggleDotFileBool,
}
Expand Down Expand Up @@ -127,8 +127,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.fullWidth = msg.Width
m.fileModel.maxFilePanel = (msg.Width - 20) / 24

m.helpMenu.height = m.fullHeight -2
m.helpMenu.width = m.fullWidth -2
m.helpMenu.height = m.fullHeight - 2
m.helpMenu.width = m.fullWidth - 2

if m.fullHeight > 32 {
m.helpMenu.height = 30
Expand Down Expand Up @@ -160,7 +160,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

m, cmd = mainKey(msg.String(), m, cmd)

if m.editorMode {
m.editorMode = false
return m, cmd
Expand Down Expand Up @@ -216,30 +216,45 @@ func (m model) View() string {
// check is the terminal size enough
if m.fullHeight < minimumHeight || m.fullWidth < minimumWidth {
return terminalSizeWarnRender(m)
} else if m.typingModal.open {
return typineModalRender(m)
} else if m.warnModal.open {
return warnModalRender(m)
} else if m.helpMenu.open {
return helpMenuRender(m)
} else {
sidebar := sidebarRender(m)

filePanel := filePanelRender(m)
}
sidebar := sidebarRender(m)

filePanel := filePanelRender(m)

mainPanel := lipgloss.JoinHorizontal(0, sidebar, filePanel)

mainPanel := lipgloss.JoinHorizontal(0, sidebar, filePanel)
processBar := processBarRender(m)

processBar := processBarRender(m)
metaData := metadataRender(m)

metaData := metadataRender(m)
clipboardBar := clipboardRender(m)

clipboardBar := clipboardRender(m)
footer := lipgloss.JoinHorizontal(0, processBar, metaData, clipboardBar)

footer := lipgloss.JoinHorizontal(0, processBar, metaData, clipboardBar)
// final render
finalRender := lipgloss.JoinVertical(0, mainPanel, footer)

// final render
finalRender := lipgloss.JoinVertical(0, mainPanel, footer)
if m.helpMenu.open {
helpMenu := helpMenuRender(m)
overlayX := m.fullWidth / 2 - m.helpMenu.width / 2
overlayY := m.fullHeight / 2 - m.helpMenu.height / 2

return PlaceOverlay(overlayX, overlayY, helpMenu, finalRender)
}

if m.typingModal.open {
typingModal := typineModalRender(m)
overlayX := m.fullWidth / 2 - modalWidth / 2
overlayY := m.fullHeight / 2 - modalHeight / 2
return PlaceOverlay(overlayX, overlayY, typingModal, finalRender)
}

return lipgloss.JoinVertical(lipgloss.Top, finalRender)
if m.warnModal.open {
warnModal := warnModalRender(m)
overlayX := m.fullWidth / 2 - modalWidth / 2
overlayY := m.fullHeight / 2 - modalHeight / 2
return PlaceOverlay(overlayX, overlayY, warnModal, finalRender)
}
return finalRender
}
9 changes: 5 additions & 4 deletions src/components/model_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,23 +335,24 @@ func typineModalRender(m model) string {
confirm := modalConfirm.Render(" (" + hotkeys.Confirm[0] + ") New File ")
cancel := modalCancel.Render(" (" + hotkeys.Cancel[0] + ") Cancel ")
tip := confirm + lipgloss.NewStyle().Background(modalBGColor).Render(" ") + cancel
return fullScreenStyle(m.fullHeight, m.fullWidth).Render(modalBorderStyle(modalHeight, modalWidth).Render(fileLocation + "\n" + m.typingModal.textInput.View() + "\n\n" + tip))
return modalBorderStyle(modalHeight, modalWidth).Render(fileLocation + "\n" + m.typingModal.textInput.View() + "\n\n" + tip)
} else {
fileLocation := filePanelTopDirectoryIconStyle.Render("  ") + filePanelTopPathStyle.Render(truncateTextBeginning(m.typingModal.location+"/"+m.typingModal.textInput.Value(), modalWidth-4)) + "\n"
confirm := modalConfirm.Render(" (" + hotkeys.Confirm[0] + ") New Folder ")
cancel := modalCancel.Render(" (" + hotkeys.Cancel[0] + ") Cancel ")
tip := confirm + lipgloss.NewStyle().Background(modalBGColor).Render(" ") + cancel
return fullScreenStyle(m.fullHeight, m.fullWidth).Render(modalBorderStyle(modalHeight, modalWidth).Render(fileLocation + "\n" + m.typingModal.textInput.View() + "\n\n" + tip))
return modalBorderStyle(modalHeight, modalWidth).Render(fileLocation + "\n" + m.typingModal.textInput.View() + "\n\n" + tip)
}
}


func warnModalRender(m model) string {
title := m.warnModal.title
content := m.warnModal.content
confirm := modalCancel.Render(" (" + hotkeys.Confirm[0] + ") Confirm ")
cancel := modalCancel.Render(" (" + hotkeys.Cancel[0] + ") Cancel ")
tip := confirm + lipgloss.NewStyle().Background(modalBGColor).Render(" ") + cancel
return fullScreenStyle(m.fullHeight, m.fullWidth).Render(modalBorderStyle(modalHeight, modalWidth).Render(title + "\n\n" + content + "\n\n" + tip))
return modalBorderStyle(modalHeight, modalWidth).Render(title + "\n\n" + content + "\n\n" + tip)
}

func helpMenuRender(m model) string {
Expand Down Expand Up @@ -425,5 +426,5 @@ func helpMenuRender(m model) string {

bottomBorder := generateFooterBorder(fmt.Sprintf("%s/%s", strconv.Itoa(m.helpMenu.cursor+1 - cursorBeenTitleCount), strconv.Itoa(len(m.helpMenu.data)-totalTitleCount)), m.helpMenu.width-2)

return fullScreenStyle(m.fullHeight, m.fullWidth).Render(helpMenuModalBorderStyle(m.helpMenu.height, m.helpMenu.width, bottomBorder).Render(helpMenuContent))
return helpMenuModalBorderStyle(m.helpMenu.height, m.helpMenu.width, bottomBorder).Render(helpMenuContent)
}
168 changes: 168 additions & 0 deletions src/components/over_place.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package components

import (
"bytes"
"strings"

charmansi "github.com/charmbracelet/x/exp/term/ansi"
"github.com/mattn/go-runewidth"
ansi "github.com/muesli/reflow/ansi"
"github.com/muesli/reflow/truncate"
"github.com/muesli/termenv"
)

// whitespace is a whitespace renderer.
type whitespace struct {
style termenv.Style
chars string
}

type WhitespaceOption func(*whitespace)

// Render whitespaces.
func (w whitespace) render(width int) string {
if w.chars == "" {
w.chars = " "
}

r := []rune(w.chars)
j := 0
b := strings.Builder{}

// Cycle through runes and print them into the whitespace.
for i := 0; i < width; {
b.WriteRune(r[j])
j++
if j >= len(r) {
j = 0
}
i += charmansi.StringWidth(string(r[j]))
}

// Fill any extra gaps white spaces. This might be necessary if any runes
// are more than one cell wide, which could leave a one-rune gap.
short := width - charmansi.StringWidth(b.String())
if short > 0 {
b.WriteString(strings.Repeat(" ", short))
}

return w.style.Styled(b.String())
}

// PlaceOverlay places fg on top of bg.
func PlaceOverlay(x, y int, fg, bg string, opts ...WhitespaceOption) string {
fgLines, fgWidth := getLines(fg)
bgLines, bgWidth := getLines(bg)
bgHeight := len(bgLines)
fgHeight := len(fgLines)

if fgWidth >= bgWidth && fgHeight >= bgHeight {
// FIXME: return fg or bg?
return fg
}
// TODO: allow placement outside of the bg box?
x = clamp(x, 0, bgWidth-fgWidth)
y = clamp(y, 0, bgHeight-fgHeight)

ws := &whitespace{}
for _, opt := range opts {
opt(ws)
}

var b strings.Builder
for i, bgLine := range bgLines {
if i > 0 {
b.WriteByte('\n')
}
if i < y || i >= y+fgHeight {
b.WriteString(bgLine)
continue
}

pos := 0
if x > 0 {
left := truncate.String(bgLine, uint(x))
pos = ansi.PrintableRuneWidth(left)
b.WriteString(left)
if pos < x {
b.WriteString(ws.render(x - pos))
pos = x
}
}

fgLine := fgLines[i-y]
b.WriteString(fgLine)
pos += ansi.PrintableRuneWidth(fgLine)

right := cutLeft(bgLine, pos)
bgWidth := ansi.PrintableRuneWidth(bgLine)
rightWidth := ansi.PrintableRuneWidth(right)
if rightWidth <= bgWidth-pos {
b.WriteString(ws.render(bgWidth - rightWidth - pos))
}

b.WriteString(right)
}

return b.String()
}

// cutLeft cuts printable characters from the left.
// This function is heavily based on muesli's ansi and truncate packages.
func cutLeft(s string, cutWidth int) string {
var (
pos int
isAnsi bool
ab bytes.Buffer
b bytes.Buffer
)
for _, c := range s {
var w int
if c == ansi.Marker || isAnsi {
isAnsi = true
ab.WriteRune(c)
if ansi.IsTerminator(c) {
isAnsi = false
if bytes.HasSuffix(ab.Bytes(), []byte("[0m")) {
ab.Reset()
}
}
} else {
w = runewidth.RuneWidth(c)
}

if pos >= cutWidth {
if b.Len() == 0 {
if ab.Len() > 0 {
b.Write(ab.Bytes())
}
if pos-cutWidth > 1 {
b.WriteByte(' ')
continue
}
}
b.WriteRune(c)
}
pos += w
}
return b.String()
}

func clamp(v, lower, upper int) int {
return min(max(v, lower), upper)
}

// Split a string into lines, additionally returning the size of the widest
// line.
func getLines(s string) (lines []string, widest int) {
lines = strings.Split(s, "\n")

for _, l := range lines {
w := charmansi.StringWidth(l)
if widest < w {
widest = w
}
}

return lines, widest
}
44 changes: 43 additions & 1 deletion src/components/string_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,46 @@ func clipboardPrettierName(name string, width int, isDir bool, isSelected bool)
Render(style.icon + " ") +
filePanelStyle.Render(truncateTextBeginning(name, width))
}
}
}

// func placeOverlay(x, y int,background, placeModal string) string {
// lines := strings.Split(placeModal, "\n")
// lines = lines
// re := regexp.MustCompile(`\x1b\[[0-9;]*[mK]`)

// // 示例字符串
// str := "┏A我"

// // 使用 FindAllStringIndex 找出所有匹配的位置
// indexes := re.FindAllStringIndex(str, -1)
// outPutLog(str)
// // 檢查是否找到匹配
// if indexes != nil {
// for _, loc := range indexes {
// loc = mapCoords(str, loc)
// outPutLog(fmt.Sprintf("匹配的開始位置: %d, 結束位置: %d", loc[0], loc[1]))
// }
// } else {
// outPutLog("沒有找到匹配")
// }

// return ""
// }

// func mapCoords(s string, byteCoords []int) (graphemeCoords []int) {
// graphemeCoords = make([]int, 2)
// gr := uniseg.NewGraphemes(s)
// graphemeIndex := -1
// for gr.Next() {
// graphemeIndex++
// a, b := gr.Positions()
// if a == byteCoords[0] {
// graphemeCoords[0] = graphemeIndex
// }
// if b == byteCoords[1] {
// graphemeCoords[1] = graphemeIndex + 1
// break
// }
// }
// return
// }
7 changes: 4 additions & 3 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,26 @@ require (
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-runewidth v0.0.15
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/reflow v0.3.0
github.com/pelletier/go-toml/v2 v2.2.1
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
Loading

0 comments on commit 53d5bcc

Please sign in to comment.