Improve input method

This commit is contained in:
akiyosi 2022-10-23 15:25:12 +09:00
parent 0782ae64ec
commit eb86edf1ff
7 changed files with 399 additions and 41 deletions

View file

@ -2,7 +2,6 @@ package editor
import (
"math"
"runtime"
"github.com/akiyosi/goneovim/util"
"github.com/therecipe/qt/core"
@ -320,10 +319,12 @@ func (c *Cursor) getDrawingPos(x, y, xprime, yprime, deltax, deltay float64) (fl
func (c *Cursor) move() {
X, Y := c.getDrawingPos(c.x, c.y, c.xprime, c.yprime, c.deltax, c.deltay)
c.Move2(
int(X),
int(Y),
)
iX := int(X)
iY := int(Y)
iX += c.ws.screen.tooltip.cursorVisualPos
c.Move2(iX, iY)
}
func (c *Cursor) updateFont(targetWin *Window, font *Font) {
@ -670,9 +671,7 @@ func (c *Cursor) redraw() {
c.paint()
// Fix #119: Wrong candidate window position when using ibus
if runtime.GOOS == "linux" {
gui.QGuiApplication_InputMethod().Update(core.Qt__ImCursorRectangle)
}
editor.app.InputMethod().Update(core.Qt__ImCursorRectangle)
}
// paint() is to request update cursor widget.

82
editor/handleime.cpp Normal file
View file

@ -0,0 +1,82 @@
#include "handleime.h"
#include <QInputMethodEvent>
int selectionLengthInPreeditStrOnDarwin(void* ptr, int cursorpos) {
QInputMethodEvent* event = static_cast<QInputMethodEvent*>(ptr);
QList<QInputMethodEvent::Attribute> attributes;
attributes = {};
attributes = event->attributes();
int ret = attributes.size();
for (int i = 0; i < attributes.size(); i++) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::TextFormat) {
if (a.start + a.length == cursorpos) {
ret = a.length;
}
}
}
return ret;
}
int selectionLengthInPreeditStr(void* ptr, int cursorpos) {
QInputMethodEvent* event = static_cast<QInputMethodEvent*>(ptr);
QList<QInputMethodEvent::Attribute> attributes;
attributes = {};
attributes = event->attributes();
int ret = attributes.size();
for (int i = 0; i < attributes.size(); i++) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::TextFormat) {
if (a.start == cursorpos) {
ret = a.length;
}
}
}
return ret;
}
int cursorPosInPreeditStr(void* ptr) {
QInputMethodEvent* event = static_cast<QInputMethodEvent*>(ptr);
QList<QInputMethodEvent::Attribute> attributes;
attributes = {};
attributes = event->attributes();
int ret = attributes.size();
for (int i = 0; i < attributes.size(); i++) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::Cursor) {
ret = a.start;
}
}
return ret;
}

22
editor/handleime.go Normal file
View file

@ -0,0 +1,22 @@
package editor
//#include <stdint.h>
//#include "handleime.h"
import "C"
import (
"runtime"
"github.com/therecipe/qt/gui"
)
func selectionPosInPreeditStr(event *gui.QInputMethodEvent) (cursorPos, selectionLength int) {
cursorPos = int(C.cursorPosInPreeditStr(event.Pointer()))
if runtime.GOOS == "darwin" {
selectionLength = int(C.selectionLengthInPreeditStrOnDarwin(event.Pointer(), C.int(cursorPos)))
} else {
selectionLength = int(C.selectionLengthInPreeditStr(event.Pointer(), C.int(cursorPos)))
}
return
}

21
editor/handleime.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#ifndef GO_HANDLEIME_H
#define GO_HANDLEIME_H
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
int selectionLengthInPreeditStr(void* ptr, int cursorpos);
int selectionLengthInPreeditStrOnDarwin(void* ptr, int cursorpos);
int cursorPosInPreeditStr(void* ptr);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,6 +1,10 @@
package editor
import (
"fmt"
"math"
"runtime"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
)
@ -8,6 +12,11 @@ import (
// IMETooltip is the tooltip for Input Method Editor
type IMETooltip struct {
Tooltip
cursorPos int
selectionLength int
cursorVisualPos int
}
func (i *IMETooltip) setQpainterFont(p *gui.QPainter) {
@ -17,12 +26,22 @@ func (i *IMETooltip) setQpainterFont(p *gui.QPainter) {
if i.font.fontNew == nil {
return
}
// if i.s.ws.palette.widget.IsVisible() {
// p.SetFont(
// gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false),
// )
// } else {
// p.SetFont(i.font.fontNew)
// }
p.SetFont(i.getFont())
}
func (i *IMETooltip) getFont() *gui.QFont {
if i.s.ws.palette.widget.IsVisible() {
p.SetFont(
gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false),
)
return gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false)
} else {
p.SetFont(i.font.fontNew)
return i.font.fontNew
}
}
@ -56,11 +75,159 @@ func (i *IMETooltip) getNthWidthAndShift(n int) (float64, int) {
func (i *IMETooltip) paint(event *gui.QPaintEvent) {
p := gui.NewQPainter2(i)
i.drawBackground(p, i.setQpainterFont, i.getNthWidthAndShift)
i.drawForeground(p, i.setQpainterFont, i.getNthWidthAndShift)
i.drawPreeditString(p, i.getNthWidthAndShift)
p.DestroyQPainter()
}
func (i *IMETooltip) drawBackground(p *gui.QPainter, f func(*gui.QPainter), g func(int) (float64, int)) {
// f(p)
// p.SetPen2(i.s.ws.foreground.QColor())
var bgColor *RGBA
if i.s.ws.screenbg == "dark" {
bgColor = warpColor(editor.colors.bg, -30)
} else {
bgColor = warpColor(editor.colors.bg, 30)
}
var height, lineHeight int
if i.s.ws.palette.widget.IsVisible() {
height = int(
math.Ceil(
gui.NewQFontMetricsF(
gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false),
).Height(),
),
)
lineHeight = height
} else {
height = i.s.tooltip.font.height
lineHeight = i.s.tooltip.font.lineHeight
}
if i.text != "" {
r := []rune(i.text)
var x, rectWidth float64
for k := 0; k < len(r); k++ {
width, _ := g(k)
x += width
y := float64(lineHeight-height) / 2
nextWidth, _ := g(k + 1)
rectWidth = nextWidth
height := float64(height) * 1.1
if height > float64(lineHeight) {
height = float64(lineHeight)
}
// draw background
p.FillRect4(
core.NewQRectF4(
x,
y,
rectWidth,
height,
),
bgColor.QColor(),
)
}
}
}
func (i *IMETooltip) drawPreeditString(p *gui.QPainter, g func(int) (float64, int)) {
var fgColor *RGBA
if i.s.ws.screenbg == "dark" {
fgColor = warpColor(editor.colors.fg, 30)
} else {
fgColor = warpColor(editor.colors.fg, -30)
}
p.SetPen2(fgColor.QColor())
length := i.s.tooltip.selectionLength
start := i.s.tooltip.cursorPos
if runtime.GOOS == "darwin" {
start = i.s.tooltip.cursorPos - length
}
var height, lineHeight int
if i.s.ws.palette.widget.IsVisible() {
height = int(
math.Ceil(
gui.NewQFontMetricsF(
gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false),
).Height(),
),
)
lineHeight = height
} else {
height = i.s.tooltip.font.height
lineHeight = i.s.tooltip.font.lineHeight
}
if i.text != "" {
r := []rune(i.text)
var x, rectWidth float64
for k := 0; k < start+length; k++ {
if k >= len(r) {
break
}
width, shift := g(k)
x += width
if k < start {
continue
}
y := float64(lineHeight-height) / 2
nextWidth, _ := g(k + 1)
rectWidth = nextWidth
height := float64(height) * 1.1
if height > float64(lineHeight) {
height = float64(lineHeight)
}
var underlinePos float64 = 1
if i.s.ws.palette.widget.IsVisible() {
underlinePos = 2
}
// draw underline
p.FillRect4(
core.NewQRectF4(
x,
y+height-underlinePos,
rectWidth,
underlinePos,
),
fgColor.QColor(),
)
// draw preedit string
p.DrawText(
core.NewQPointF3(
float64(x),
float64(shift),
),
string(r[k]),
)
}
}
}
func (i *IMETooltip) pos() (int, int, int, int) {
var x, y, candX, candY int
ws := i.s.ws
@ -71,19 +238,21 @@ func (i *IMETooltip) pos() (int, int, int, int) {
if ws.palette == nil {
return 0, 0, 0, 0
}
win, ok := s.getWindow(s.ws.cursor.gridid)
if !ok {
return 0, 0, 0, 0
}
font := win.getFont()
if ws.palette.widget.IsVisible() {
// font := gui.NewQFont2(editor.extFontFamily, editor.extFontSize, 1, false)
// i.SetFont(font)
x = ws.palette.cursorX + ws.palette.patternPadding
y = ws.palette.patternPadding + ws.palette.padding + 1
candX = x + ws.palette.widget.Pos().X()
y = ws.palette.patternPadding + ws.palette.padding
candY = y + ws.palette.widget.Pos().Y()
} else {
win, ok := s.getWindow(s.ws.cursor.gridid)
if !ok {
return 0, 0, 0, 0
}
font := win.getFont()
i.setFont(font)
row := s.cursor[0]
col := s.cursor[1]
@ -106,6 +275,15 @@ func (i *IMETooltip) pos() (int, int, int, int) {
}
candY = (row+posy)*font.lineHeight + tablineMarginTop + tablineHeight + tablineMarginBottom
}
candX = candX + i.cursorVisualPos
editor.putLog(
fmt.Sprintf(
"IME preeditstr:: cursor pos in preeditstr: %d",
int(float64(i.cursorVisualPos)/font.cellwidth),
),
)
return x, y, candX, candY
}
@ -133,15 +311,42 @@ func (i *IMETooltip) show() {
i.SetParent(i.s.ws.palette.widget)
}
i.SetAutoFillBackground(true)
p := gui.NewQPalette()
p.SetColor2(gui.QPalette__Background, i.s.ws.background.QColor())
i.SetPalette(p)
// i.SetAutoFillBackground(true)
// p := gui.NewQPalette()
// p.SetColor2(gui.QPalette__Background, i.s.ws.background.QColor())
// i.SetPalette(p)
i.Show()
i.Raise()
}
func (i *IMETooltip) updateVirtualCursorPos() {
g := i.getNthWidthAndShift
length := i.s.tooltip.selectionLength
start := i.s.tooltip.cursorPos
var x float64
if i.text != "" {
r := []rune(i.text)
for k := 0; k < start+length; k++ {
if k > len(r) {
break
}
width, _ := g(k)
x += width
if k >= start {
break
}
}
}
i.cursorVisualPos = int(x)
}
func (i *IMETooltip) updateText(text string) {
if i.font == nil {
return

View file

@ -65,6 +65,7 @@ func newScreen() *Screen {
func (s *Screen) initInputMethodWidget() {
tooltip := NewIMETooltip(s.widget, 0)
tooltip.SetVisible(false)
tooltip.SetAttribute(core.Qt__WA_OpaquePaintEvent, true)
tooltip.ConnectPaintEvent(tooltip.paint)
tooltip.s = s
s.tooltip = tooltip
@ -1270,6 +1271,22 @@ func (s *Screen) update() {
})
}
func (s *Screen) refresh() {
s.windows.Range(func(grid, winITF interface{}) bool {
win := winITF.(*Window)
if win != nil {
// Fill entire background if background color changed
if !win.background.equals(s.ws.background) {
win.background = s.ws.background.copy()
win.fill()
}
win.Update()
}
return true
})
}
func (s *Screen) runeTextWidth(font *Font, text string) float64 {
cjk := 0
ascii := 0

View file

@ -174,13 +174,11 @@ func newWorkspace(path string) (*Workspace, error) {
}
// palette
if editor.config.Editor.ExtCmdline {
w.palette = initPalette()
w.palette.ws = w
w.palette.widget.SetParent(w.widget)
w.palette.setColor()
w.palette.hide()
}
w.palette = initPalette()
w.palette.ws = w
w.palette.widget.SetParent(w.widget)
w.palette.setColor()
w.palette.hide()
// popupmenu
if editor.config.Editor.ExtPopupmenu {
@ -2216,8 +2214,6 @@ func (w *Workspace) handleRPCGui(updates []interface{}) {
}
win.doGetSnapshot = true
}
// w.maxLineDelta = util.ReflectToInt(updates[1]) - w.maxLine
// w.maxLine = util.ReflectToInt(updates[1])
default:
fmt.Println("unhandled Gui event", event)
@ -2742,24 +2738,48 @@ func (w *Workspace) setFileType(args []interface{}) {
// InputMethodEvent is
func (w *Workspace) InputMethodEvent(event *gui.QInputMethodEvent) {
w.screen.tooltip.cursorPos, w.screen.tooltip.selectionLength = selectionPosInPreeditStr(event)
if event.CommitString() != "" {
w.screen.tooltip.cursorVisualPos = 0
w.nvim.Input(event.CommitString())
w.screen.tooltip.Hide()
} else {
preeditString := event.PreeditString()
if preeditString == "" {
w.screen.tooltip.Hide()
w.cursor.update()
w.screen.refresh()
} else {
w.screen.tooltip.updateText(preeditString)
w.screen.tooltip.update()
w.screen.tooltip.show()
}
w.screen.tooltip.updateVirtualCursorPos()
}
w.cursor.update()
editor.putLog(
fmt.Sprintf(
"QInputMethodEvent:: IME preeditstr: cursorpos: %d, selectionLength: %d, cursorVisualPos: %d",
w.screen.tooltip.cursorPos,
w.screen.tooltip.selectionLength,
w.screen.tooltip.cursorVisualPos,
),
)
}
// InputMethodQuery is
func (w *Workspace) InputMethodQuery(query core.Qt__InputMethodQuery) *core.QVariant {
editor.putLog(
fmt.Sprintf(
"InputMethodQuery:: query: %d", query,
),
)
if query == core.Qt__ImMicroFocus || query == core.Qt__ImCursorRectangle {
x, y, candX, candY := w.screen.tooltip.pos()
w.screen.tooltip.move(x, y)
@ -2777,17 +2797,9 @@ func (w *Workspace) InputMethodQuery(query core.Qt__InputMethodQuery) *core.QVar
}
imrect.SetRect(candX, candY+res+5, 1, w.font.lineHeight)
if w.palette != nil {
if w.palette.widget.IsVisible() {
// w.cursor.x = float64(x + w.screen.tooltip.Width())
// w.cursor.y = float64(w.palette.patternPadding + w.cursor.shift)
// w.cursor.Update()
w.cursor.Hide()
}
}
return core.NewQVariant31(imrect)
}
return core.NewQVariant()
}