akiyosi.goneovim/editor/palette.go

562 lines
13 KiB
Go
Raw Permalink Normal View History

2017-11-09 02:11:05 +00:00
package editor
2017-06-28 08:28:24 +01:00
import (
"fmt"
2018-09-02 15:55:24 +09:00
"math"
2021-12-07 23:21:16 +09:00
"path/filepath"
2021-12-10 21:24:04 +09:00
"runtime"
2021-12-07 23:21:16 +09:00
"sort"
"strings"
2017-06-28 08:28:24 +01:00
2020-09-26 12:04:36 +09:00
"github.com/akiyosi/goneovim/util"
2023-07-16 00:53:36 +09:00
"github.com/akiyosi/qt/core"
"github.com/akiyosi/qt/gui"
"github.com/akiyosi/qt/svg"
"github.com/akiyosi/qt/widgets"
2017-06-28 08:28:24 +01:00
)
2021-12-10 21:24:04 +09:00
// Palette is the popup for cmdline
2017-06-28 08:28:24 +01:00
type Palette struct {
2018-01-05 08:07:29 +00:00
ws *Workspace
2021-12-10 21:24:04 +09:00
background *RGBA
2017-06-28 11:06:47 +01:00
widget *widgets.QWidget
2021-12-10 21:24:04 +09:00
foreground *RGBA
scrollCol *widgets.QWidget
scrollBar *widgets.QWidget
patternWidget *widgets.QWidget
2017-06-28 11:06:47 +01:00
resultWidget *widgets.QWidget
resultMainWidget *widgets.QWidget
2021-12-10 21:24:04 +09:00
pattern *widgets.QLabel
inactiveFg *RGBA
2017-06-28 11:06:47 +01:00
resultType string
2021-12-10 21:24:04 +09:00
patternText string
2017-06-28 11:06:47 +01:00
itemTypes []string
2021-12-10 21:24:04 +09:00
resultItems []*PaletteResultItem
2017-06-28 11:06:47 +01:00
showTotal int
2021-12-10 21:24:04 +09:00
itemHeight int
2017-06-28 11:06:47 +01:00
patternPadding int
2021-12-10 21:24:04 +09:00
max int
2017-06-28 11:06:47 +01:00
scrollBarPos int
2021-12-10 21:24:04 +09:00
width int
padding int
cursorX int
isHTMLText bool
hidden bool
2017-06-28 08:28:24 +01:00
}
// PaletteResultItem is the result item
type PaletteResultItem struct {
2018-01-05 08:07:29 +00:00
p *Palette
2021-12-10 21:24:04 +09:00
widget *widgets.QWidget
2017-06-28 08:28:24 +01:00
icon *svg.QSvgWidget
base *widgets.QLabel
2021-12-10 21:24:04 +09:00
iconType string
2017-06-28 08:28:24 +01:00
baseText string
2021-12-10 21:24:04 +09:00
iconHidden bool
hidden bool
2017-06-28 08:28:24 +01:00
selected bool
}
func initPalette() *Palette {
width := 600
2019-09-08 21:40:15 +09:00
padding := 8
2017-06-28 08:28:24 +01:00
mainLayout := widgets.NewQVBoxLayout()
mainLayout.SetContentsMargins(0, 0, 0, 0)
mainLayout.SetSpacing(0)
2017-11-09 01:41:26 +00:00
mainLayout.SetSizeConstraint(widgets.QLayout__SetMinAndMaxSize)
2017-06-28 08:28:24 +01:00
widget := widgets.NewQWidget(nil, 0)
widget.SetLayout(mainLayout)
widget.SetContentsMargins(1, 1, 1, 1)
2017-11-09 01:41:26 +00:00
// widget.SetFixedWidth(width)
2017-06-28 08:28:24 +01:00
widget.SetObjectName("palette")
2019-09-08 21:40:15 +09:00
2019-10-18 00:08:56 +09:00
widget.SetGraphicsEffect(util.DropShadow(0, 15, 130, 120))
2017-06-28 08:28:24 +01:00
resultMainLayout := widgets.NewQHBoxLayout()
resultMainLayout.SetContentsMargins(0, 0, 0, 0)
resultMainLayout.SetSpacing(0)
2017-11-09 01:41:26 +00:00
resultMainLayout.SetSizeConstraint(widgets.QLayout__SetMinAndMaxSize)
2017-06-28 08:28:24 +01:00
resultLayout := widgets.NewQVBoxLayout()
resultLayout.SetContentsMargins(0, 0, 0, 0)
resultLayout.SetSpacing(0)
2017-11-09 01:41:26 +00:00
resultLayout.SetSizeConstraint(widgets.QLayout__SetMinAndMaxSize)
2017-06-28 08:28:24 +01:00
resultWidget := widgets.NewQWidget(nil, 0)
resultWidget.SetLayout(resultLayout)
2019-10-26 03:00:14 +09:00
resultWidget.SetStyleSheet("background-color: rgba(0, 0, 0, 0); white-space: pre-wrap;")
2017-06-28 08:28:24 +01:00
resultWidget.SetContentsMargins(0, 0, 0, 0)
scrollCol := widgets.NewQWidget(nil, 0)
scrollCol.SetContentsMargins(0, 0, 0, 0)
scrollCol.SetFixedWidth(5)
scrollBar := widgets.NewQWidget(scrollCol, 0)
scrollBar.SetFixedWidth(5)
resultMainWidget := widgets.NewQWidget(nil, 0)
2019-03-21 18:38:53 +09:00
resultMainWidget.SetStyleSheet(" * { background-color: rgba(0, 0, 0, 0); }")
2017-06-28 08:28:24 +01:00
resultMainWidget.SetContentsMargins(0, 0, 0, 0)
resultMainLayout.AddWidget(resultWidget, 0, 0)
resultMainLayout.AddWidget(scrollCol, 0, 0)
resultMainWidget.SetLayout(resultMainLayout)
pattern := widgets.NewQLabel(nil, 0)
pattern.SetContentsMargins(padding, padding, padding, padding)
2017-11-09 01:41:26 +00:00
pattern.SetFixedWidth(width - padding*2)
pattern.SetSizePolicy2(widgets.QSizePolicy__Preferred, widgets.QSizePolicy__Maximum)
2017-06-28 08:28:24 +01:00
patternLayout := widgets.NewQVBoxLayout()
patternLayout.AddWidget(pattern, 0, 0)
patternLayout.SetContentsMargins(0, 0, 0, 0)
patternLayout.SetSpacing(0)
2017-11-09 01:41:26 +00:00
patternLayout.SetSizeConstraint(widgets.QLayout__SetMinAndMaxSize)
2017-06-28 08:28:24 +01:00
patternWidget := widgets.NewQWidget(nil, 0)
patternWidget.SetLayout(patternLayout)
patternWidget.SetContentsMargins(padding, padding, padding, padding)
mainLayout.AddWidget(patternWidget, 0, 0)
mainLayout.AddWidget(resultMainWidget, 0, 0)
2018-01-05 08:07:29 +00:00
palette := &Palette{
width: width,
widget: widget,
2019-08-31 01:47:08 +09:00
padding: padding,
2018-01-05 08:07:29 +00:00
resultWidget: resultWidget,
resultMainWidget: resultMainWidget,
pattern: pattern,
patternPadding: padding,
patternWidget: patternWidget,
scrollCol: scrollCol,
scrollBar: scrollBar,
}
2017-06-28 08:28:24 +01:00
resultItems := []*PaletteResultItem{}
2019-03-13 17:35:59 +09:00
max := editor.config.Palette.MaxNumberOfResultItems
2017-06-28 08:28:24 +01:00
for i := 0; i < max; i++ {
itemWidget := widgets.NewQWidget(nil, 0)
itemWidget.SetContentsMargins(0, 0, 0, 0)
2019-03-05 23:09:22 +09:00
itemLayout := util.NewVFlowLayout(padding, padding*2, 0, 0, 9999)
2017-11-09 01:41:26 +00:00
itemLayout.SetSizeConstraint(widgets.QLayout__SetMinAndMaxSize)
2017-06-28 08:28:24 +01:00
itemWidget.SetLayout(itemLayout)
2019-10-26 03:00:14 +09:00
// itemWidget.SetStyleSheet("background-color: rgba(0, 0, 0, 0);")
2017-06-28 08:28:24 +01:00
resultLayout.AddWidget(itemWidget, 0, 0)
icon := svg.NewQSvgWidget(nil)
2018-11-08 18:10:12 +09:00
icon.SetFixedWidth(editor.iconSize - 1)
icon.SetFixedHeight(editor.iconSize - 1)
2017-06-28 08:28:24 +01:00
icon.SetContentsMargins(0, 0, 0, 0)
2019-10-26 03:00:14 +09:00
// icon.SetStyleSheet("background-color: rgba(0, 0, 0, 0);")
2017-06-28 08:28:24 +01:00
base := widgets.NewQLabel(nil, 0)
base.SetText("base")
base.SetContentsMargins(0, padding, 0, padding)
2019-10-26 03:00:14 +09:00
// base.SetStyleSheet("background-color: rgba(0, 0, 0, 0); white-space: pre-wrap;")
base.SetSizePolicy2(widgets.QSizePolicy__Preferred, widgets.QSizePolicy__Maximum)
2017-06-28 08:28:24 +01:00
itemLayout.AddWidget(icon)
itemLayout.AddWidget(base)
resultItem := &PaletteResultItem{
2018-01-05 08:07:29 +00:00
p: palette,
2017-06-28 08:28:24 +01:00
widget: itemWidget,
icon: icon,
base: base,
}
resultItems = append(resultItems, resultItem)
}
2018-01-05 08:07:29 +00:00
palette.max = max
palette.resultItems = resultItems
2017-06-28 08:28:24 +01:00
return palette
}
func (p *Palette) setParent(win *Window) {
if win.isExternal {
p.widget.SetParent(win.extwin)
} else {
p.widget.SetParent(p.ws.widget)
}
}
2018-12-27 21:03:21 +09:00
func (p *Palette) setColor() {
if p.foreground.equals(editor.colors.widgetFg) && p.background.equals(editor.colors.widgetBg) && p.inactiveFg.equals(editor.colors.inactiveFg) {
return
}
p.foreground = editor.colors.widgetFg
p.background = editor.colors.widgetBg
p.inactiveFg = editor.colors.inactiveFg
fg := p.foreground.String()
2019-02-17 18:00:09 +09:00
bg := editor.colors.widgetBg
if bg.HSV().V < 0.1 {
bg = warpColor(bg, -15)
} else if bg.HSV().V < 0.1 {
bg = warpColor(bg, -5)
}
2019-03-21 14:24:54 +09:00
inactiveFg := editor.colors.inactiveFg
2019-02-17 18:00:09 +09:00
transparent := transparent() * transparent()
2019-11-23 03:46:36 +09:00
if editor.config.Palette.Transparent < 1.0 {
transparent = editor.config.Palette.Transparent * editor.config.Palette.Transparent
}
2019-02-17 18:00:09 +09:00
p.widget.SetStyleSheet(fmt.Sprintf(" .QWidget { background-color: rgba(%d, %d, %d, %f); } * { color: %s; } ", bg.R, bg.G, bg.B, transparent, fg))
2019-03-21 14:24:54 +09:00
p.scrollBar.SetStyleSheet(fmt.Sprintf("background-color: rgba(%d, %d, %d, %f);", inactiveFg.R, inactiveFg.G, inactiveFg.B, transparent))
2019-08-10 06:23:48 +09:00
for _, item := range p.resultItems {
item.widget.SetStyleSheet(fmt.Sprintf(" .QWidget { background-color: rgba(0, 0, 0, 0.0); } * { color: %s; } ", fg))
}
2019-03-21 18:38:53 +09:00
if transparent < 1.0 {
p.patternWidget.SetStyleSheet("background-color: rgba(0, 0, 0, 0);")
p.pattern.SetStyleSheet("background-color: rgba(0, 0, 0, 0);")
} else {
p.pattern.SetStyleSheet(fmt.Sprintf("background-color: %s;", bg.String()))
}
2018-12-27 21:03:21 +09:00
}
2017-06-28 08:28:24 +01:00
func (p *Palette) resize() {
parentWidget := p.widget.ParentWidget()
eWidth := 0
if parentWidget == nil {
eWidth = editor.window.Width()
} else {
eWidth = parentWidget.Width()
}
2020-08-11 00:42:38 +09:00
width := int(math.Trunc(float64(eWidth) * 0.7))
2019-09-08 21:40:15 +09:00
cursorBoundary := p.padding*4 + p.textLength() + p.patternPadding
2019-11-21 16:29:43 +09:00
if cursorBoundary > width {
width = cursorBoundary
2018-01-11 06:11:34 +00:00
}
2020-08-11 00:42:38 +09:00
if width > eWidth {
width = eWidth
2018-10-31 12:12:57 +09:00
p.pattern.SetAlignment(core.Qt__AlignRight | core.Qt__AlignCenter)
2020-08-11 00:42:38 +09:00
} else if width <= eWidth {
2018-10-31 12:12:57 +09:00
if p.pattern.Alignment() != core.Qt__AlignLeft {
p.pattern.SetAlignment(core.Qt__AlignLeft)
2018-09-02 15:55:24 +09:00
}
2018-10-31 12:12:57 +09:00
}
2017-06-28 08:28:24 +01:00
2019-11-21 16:29:43 +09:00
if p.width == width {
return
}
p.width = width
2019-09-08 21:40:15 +09:00
p.pattern.SetFixedWidth(p.width - p.padding*2)
2018-10-31 12:12:57 +09:00
p.widget.SetMaximumWidth(p.width)
p.widget.SetMinimumWidth(p.width)
2018-09-02 15:55:24 +09:00
2020-08-11 00:42:38 +09:00
x := eWidth - p.width
2018-10-31 12:12:57 +09:00
if x < 0 {
x = 0
}
2018-12-05 12:25:22 +09:00
p.widget.Move2(x/2, 10)
2018-09-02 15:55:24 +09:00
2020-07-30 22:50:02 +09:00
p.showTotal = 0
for i := p.showTotal; i < len(p.resultItems); i++ {
p.resultItems[i].hide()
}
}
func (p *Palette) resizeResultItems() {
if p.showTotal != 0 {
return
}
2018-10-31 12:12:57 +09:00
itemHeight := p.resultItems[0].widget.SizeHint().Height()
p.itemHeight = itemHeight
2019-03-13 17:35:59 +09:00
p.showTotal = int(float64(p.ws.height)/float64(itemHeight)*editor.config.Palette.AreaRatio) - 1
2017-06-28 08:28:24 +01:00
}
func (p *Palette) show() {
2017-11-09 01:41:26 +00:00
if !p.hidden {
return
}
p.setColor()
2020-07-30 22:50:02 +09:00
p.resizeResultItems()
2017-11-09 01:41:26 +00:00
p.hidden = false
2018-09-02 15:55:24 +09:00
p.widget.Raise()
p.widget.SetWindowOpacity(1.0)
2020-12-16 00:45:14 +09:00
p.resize()
p.widget.Show()
2017-06-28 08:28:24 +01:00
}
func (p *Palette) hide() {
2017-11-09 01:41:26 +00:00
if p.hidden {
return
}
p.hidden = true
2017-06-28 08:28:24 +01:00
p.widget.Hide()
}
2017-06-28 11:06:47 +01:00
func (p *Palette) setPattern(text string) {
p.patternText = text
p.pattern.SetText(text)
}
2017-06-28 08:28:24 +01:00
func (p *Palette) cursorMove(x int) {
2019-09-08 21:40:15 +09:00
X := p.textLength()
2019-11-21 16:29:43 +09:00
var stickOutLen int
2019-09-29 00:17:37 +09:00
boundary := p.pattern.Width() - (p.padding * 2)
2019-09-08 21:40:15 +09:00
if X >= boundary {
2019-11-21 16:29:43 +09:00
stickOutLen = X - boundary
}
pos := p.cursorPos(x) - stickOutLen
if pos < 0 {
pos = 0
2019-09-08 21:40:15 +09:00
}
2019-11-21 16:29:43 +09:00
p.cursorX = pos
2021-05-27 21:41:50 +09:00
px := float64(p.cursorX + p.patternPadding)
2021-12-23 11:30:31 +09:00
py := float64(p.patternPadding + p.ws.cursor.horizontalShift)
2021-05-27 21:41:50 +09:00
c := p.ws.cursor
if !c.isInPalette {
c.SetParent(p.pattern)
p.ws.cursor.isInPalette = true
2021-05-27 21:41:50 +09:00
}
if !(c.x == px && c.y == py) {
2021-10-26 21:30:39 +09:00
c.xprime = c.x
c.yprime = c.y
2021-05-27 21:41:50 +09:00
c.x = px
c.y = py
c.doAnimate = true
c.animateMove()
}
2022-06-21 00:30:31 +09:00
p.ws.cursor.update()
2020-02-24 18:15:38 +09:00
p.redrawAllContentInWindows()
}
func (p *Palette) redrawAllContentInWindows() {
if runtime.GOOS != "windows" {
return
}
p.hide()
p.resultWidget.Hide()
p.resultMainWidget.Hide()
p.show()
p.resultWidget.Show()
p.resultMainWidget.Show()
2019-09-08 21:40:15 +09:00
}
2019-09-15 23:09:46 +09:00
func (p *Palette) updateFont() {
p.widget.SetFont(p.ws.screen.font.qfont)
p.pattern.SetFont(p.ws.screen.font.qfont)
2019-09-15 23:09:46 +09:00
}
2019-09-08 21:40:15 +09:00
func (p *Palette) textLength() int {
font := gui.NewQFontMetricsF(editor.font.qfont)
2019-09-09 23:11:32 +09:00
l := 0
if p.isHTMLText {
t := gui.NewQTextDocument(nil)
t.SetHtml(p.patternText)
l = int(
font.HorizontalAdvance(
t.ToPlainText(),
-1,
),
)
} else {
l = int(
font.HorizontalAdvance(
p.patternText,
-1,
),
)
}
return l
2017-06-28 08:28:24 +01:00
}
2019-09-16 18:53:27 +09:00
func (p *Palette) cursorPos(x int) int {
font := gui.NewQFontMetricsF(p.ws.screen.font.qfont)
2019-09-16 18:53:27 +09:00
l := 0
if p.isHTMLText {
t := gui.NewQTextDocument(nil)
2019-09-16 19:28:02 +09:00
t.SetHtml(p.patternText)
2019-09-16 18:53:27 +09:00
l = int(
font.HorizontalAdvance(
2019-09-16 19:28:02 +09:00
t.ToPlainText()[:x],
2019-09-16 18:53:27 +09:00
-1,
),
)
} else {
l = int(
font.HorizontalAdvance(
p.patternText[:x],
-1,
),
)
}
return l
}
2017-06-28 08:28:24 +01:00
func (p *Palette) showSelected(selected int) {
if p.resultType == "file_line" {
n := 0
for i := 0; i <= selected; i++ {
for n++; n < len(p.itemTypes) && p.itemTypes[n] == "file"; n++ {
}
}
selected = n
}
for i, resultItem := range p.resultItems {
resultItem.setSelected(selected == i)
}
}
func (f *PaletteResultItem) update() {
2019-02-17 18:00:09 +09:00
c := editor.colors.selectedBg
// transparent := editor.config.Editor.Transparent
transparent := transparent()
2017-06-28 08:28:24 +01:00
if f.selected {
2019-02-17 18:00:09 +09:00
f.widget.SetStyleSheet(fmt.Sprintf(".QWidget {background-color: rgba(%d, %d, %d, %f);}", c.R, c.G, c.B, transparent))
2017-06-28 08:28:24 +01:00
} else {
f.widget.SetStyleSheet("")
}
2019-10-26 03:00:14 +09:00
// f.p.widget.Hide()
// f.p.widget.Show()
2019-02-17 18:00:09 +09:00
2017-06-28 08:28:24 +01:00
}
func (f *PaletteResultItem) setSelected(selected bool) {
if f.selected == selected {
return
}
f.selected = selected
f.update()
}
func (f *PaletteResultItem) show() {
2017-11-09 01:41:26 +00:00
// if f.hidden {
f.hidden = false
f.widget.Show()
// }
2017-06-28 08:28:24 +01:00
}
func (f *PaletteResultItem) hide() {
if !f.hidden {
f.hidden = true
f.widget.Hide()
}
}
func (f *PaletteResultItem) setItem(text string, itemType string, match []int) {
iconType := ""
path := false
if itemType == "dir" {
iconType = "folder"
path = true
} else if itemType == "file" {
iconType = getFileType(text)
path = true
} else if itemType == "file_line" {
iconType = "empty"
}
if iconType != "" {
if iconType != f.iconType {
f.iconType = iconType
f.updateIcon()
}
f.showIcon()
} else {
f.hideIcon()
}
formattedText := formatText(text, match, path)
if formattedText != f.baseText {
f.baseText = formattedText
f.base.SetText(f.baseText)
}
}
2021-12-07 23:21:16 +09:00
func formatText(text string, matchIndex []int, path bool) string {
sort.Ints(matchIndex)
color := ""
if editor.colors.matchFg != nil {
color = editor.colors.matchFg.Hex()
}
match := len(matchIndex) > 0
if !path || strings.HasPrefix(text, "term://") {
formattedText := ""
i := 0
for _, char := range text {
if color != "" && len(matchIndex) > 0 && i == matchIndex[0] {
formattedText += fmt.Sprintf("<font color='%s'>%s</font>", color, string(char))
matchIndex = matchIndex[1:]
} else if color != "" && match {
switch string(char) {
case " ":
formattedText += "&nbsp;"
case "\t":
formattedText += "&nbsp;&nbsp;&nbsp;&nbsp;"
case "<":
formattedText += "&lt;"
case ">":
formattedText += "&gt;"
default:
formattedText += string(char)
}
} else {
formattedText += string(char)
}
i++
}
return formattedText
}
dirText := ""
dir := filepath.Dir(text)
if dir == "." {
dir = ""
}
if dir != "" {
i := strings.Index(text, dir)
if i != -1 {
for j, char := range dir {
if color != "" && len(matchIndex) > 0 && i+j == matchIndex[0] {
dirText += fmt.Sprintf("<font color='%s'>%s</font>", color, string(char))
matchIndex = matchIndex[1:]
} else {
dirText += string(char)
}
}
}
}
baseText := ""
base := filepath.Base(text)
if base != "" {
i := strings.LastIndex(text, base)
if i != -1 {
for j, char := range base {
if color != "" && len(matchIndex) > 0 && i+j == matchIndex[0] {
baseText += fmt.Sprintf("<font color='%s'>%s</font>", color, string(char))
matchIndex = matchIndex[1:]
} else {
baseText += string(char)
}
}
}
}
return fmt.Sprintf("%s <font color='#838383'>%s</font>", baseText, dirText)
}
2017-06-28 08:28:24 +01:00
func (f *PaletteResultItem) updateIcon() {
2018-10-13 23:14:34 +09:00
svgContent := editor.getSvg(f.iconType, nil)
2017-06-28 08:28:24 +01:00
f.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent)))
}
func (f *PaletteResultItem) showIcon() {
if f.iconHidden {
f.iconHidden = false
f.icon.Show()
}
}
func (f *PaletteResultItem) hideIcon() {
if !f.iconHidden {
f.iconHidden = true
f.icon.Hide()
}
}