Wox/wox.core/util/selection.go

117 lines
2.7 KiB
Go

package util
import (
"errors"
"strings"
"time"
"wox/util/clipboard"
"wox/util/keyboard"
)
var noSelection = errors.New("no selection")
var lastClipboardChangeTimestamp int64 = 0
type SelectionType string
const (
SelectionTypeText SelectionType = "text"
SelectionTypeFile SelectionType = "file"
)
type Selection struct {
Type SelectionType
// Only available when Type is SelectionTypeText
Text string
// Only available when Type is SelectionTypeFile
FilePaths []string
}
func InitSelection() {
clipboard.Watch(func(data clipboard.Data) {
lastClipboardChangeTimestamp = GetSystemTimestamp()
})
}
func (s *Selection) String() string {
switch s.Type {
case SelectionTypeText:
return s.Text
case SelectionTypeFile:
return strings.Join(s.FilePaths, ";")
}
return ""
}
func (s *Selection) IsEmpty() bool {
switch s.Type {
case SelectionTypeText:
return s.Text == ""
case SelectionTypeFile:
return s.FilePaths == nil || len(s.FilePaths) == 0
}
return false
}
func GetSelected() (Selection, error) {
simulateStartTimestamp := GetSystemTimestamp()
if keyboard.SimulateCopy() != nil {
return Selection{}, errors.New("error simulate ctrl c")
}
// loop to wait for clipboard data to be updated, so that we can get the clipboard data as soon as possible if the clipboard data is updated
// because sometimes clipboard data is not updated immediately after simulated ctrl c, small text data is updated immediately, but large file data is not
var clipboardDataAfter clipboard.Data
loopTimes := 10
for i := 0; i < loopTimes; i++ {
isLastLoop := loopTimes-1 == i
// wait for clipboard data to be updated
time.Sleep(50 * time.Millisecond)
clipboardData, err := clipboard.ReadFilesAndText()
if err != nil {
if isLastLoop {
return Selection{}, err
} else {
continue
}
}
// clipboard data must be updated between simulateStartTimestamp and simulateEndTimestamp
// otherwise, it means that the clipboard data is not updated by the simulated ctrl c
if lastClipboardChangeTimestamp < simulateStartTimestamp {
if isLastLoop {
return Selection{}, noSelection
} else {
continue
}
}
clipboardDataAfter = clipboardData
break
}
if clipboardDataAfter == nil {
return Selection{}, noSelection
}
switch clipboardDataAfter.GetType() {
case clipboard.ClipboardTypeText:
textData := clipboardDataAfter.(*clipboard.TextData)
return Selection{
Type: SelectionTypeText,
Text: textData.Text,
}, nil
case clipboard.ClipboardTypeFile:
fileData := clipboardDataAfter.(*clipboard.FilePathData)
return Selection{
Type: SelectionTypeFile,
FilePaths: fileData.FilePaths,
}, nil
}
return Selection{}, errors.New("unknown clipboard type")
}