Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad view title drawing with unicode characters. #157

Open
luengoi opened this issue Apr 23, 2018 · 10 comments
Open

Bad view title drawing with unicode characters. #157

luengoi opened this issue Apr 23, 2018 · 10 comments

Comments

@luengoi
Copy link

luengoi commented Apr 23, 2018

Hello, I am using this API to build the user interface for my application. I am loving it so far, it is very simple and straightforward.

However, I have found an issue when using a View title that contains unicode characters. For instance, see this example:

    ...
    v.Frame = true
    v.Title = " ⣿ HELP ⣿ "
    ...
    // Expecting  ┌ ⣿ HELP ⣿ ──────┐
    // Getting    ┌ ⣿── HELP ⣿── ──┐instead

The reason behind this issue seems to be in this line:

gocui/gui.go

Line 538 in c055c87

for i, ch := range v.Title {

Since v.Title is a string, when iterating over it you are writing over byte in the string, and because unicode encoded characters use more than one byte, we are getting that weird result.
I have tried to replace that line with the following one:

    for i, ch := range []rune(v.Title) {

And it seems to get the job done. Changing the type of View.Title to rune seems to work as well. Although the last seems to be more inconvenient for the user (it's easier to declare a string literal).
I've just learned Go a month ago so I may be saying silly things. Also there might be a more elegant solution to the issue, that is why I didn't venture to do a PR.

This is the first issue I write, I hope I've been clear enough. I don't know if this issue falls into the scope of this API but I think it would be nice to have the option to include unicode characters in the title.
Thanks again for this awesome API!

@hello-liu
Copy link

老兄,你问题解决了没有,我的是在windows下输入中文显示有问题
e

@luengoi
Copy link
Author

luengoi commented Oct 7, 2018

Hi there, with the help of Google's translator I have managed to get a grasp of what you wrote, but I don't quite understand the meaning of it. Are you asking if I have managed to solve the problem? Or are you asking about a related issue?

Anyway, I thing I should share what I learned after dealing with this issue in case someone with the same problem reads this.

At first, I didn't understood the problem. I thought that range on strings would iterate over the bytes in the string, but in reality it iterates over the runes in the string, so my reasoning was wrong. The real problem is that the first value range returns while iterating over a string (in this case, the value of the variable i) is the byte index of the rune, an thus it doesn't increment the way is expected.

To better clarify this issue, I will provide the following example (using the string I used in the first comment). Play it here.

func main() {
	for i, c := range "⣿ HELP ⣿" {
		fmt.Println(i, c)
	}
}

Outputs:

0 10495
3 32
4 72
5 69
6 76
7 80
8 32
9 10495

As you can see, the first whitespace character is at the byte index 3, because the rune '⣿' occupies 3 bytes. This is the reason why the x coordinate doesn't have the right value when setting the cells. And the reason why the fix I proposed "worked" is because range over a rune slice returns the index of the rune in the slice, therefore we get a linear increment by one unit at each iteration.

However, this arises some problems when dealing with other characters such as Chinese characters. This is because we need to know the number of cells inside the rune we want to write in order to increment the x coordinate properly (remember we are setting the individual terminal cells). To solve this issue, we can use the package runewidth.

x := v.x0 + 2
for _, ch := range v.Title {
	if x < 0 {
		continue
	} else if x > v.x1-2 || x >= g.maxX {
		break
	}
	if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
		return err
	}
       x += runewidth.RuneWidth(ch)
}

This fix, as far as I can tell, solves this issue, since we increment the x coordinate the right amount for each rune.

@hello-liu
Copy link

Thank you very much for your answer

@mitnk
Copy link

mitnk commented Nov 26, 2018

I found a dirty/quick workaround to display unicode chars - append a SPACE char after each unicode char (assume they are all 2-ascii-width):

out, err := g.View("v2")
fmt.Fprintln(out, "这 不 就 尴 尬 了 吗 ! ?")

Tested on Mac, no spaces show (either copy from) on the view screen with this.

@hello-liu
Copy link

@mitnk wa~~~ao, I've almost given up this ui,But you let me discover the new world

@mitnk
Copy link

mitnk commented Dec 3, 2018

@hello-liu Well, my solution here is for display text inside screen, not on title. On title, I think you can always to use ASCII chars only, though multiply-byte unicode char is readable-ok-ish.

@baojiweicn
Copy link

👍

@baojiweicn
Copy link

new world

@joshualley
Copy link

If you want to intput a mutibyte character, you can modify the file named edit.go of source codes.
At 60th line, you will find the func:

func (v *View) EditWrite(ch rune) {
	v.writeRune(v.cx, v.cy, ch)
	v.MoveCursor(1, 0, true)
}

Modify it like this:

func (v *View) EditWrite(ch rune) {
	v.writeRune(v.cx, v.cy, ch)
	w := runewidth.RuneWidth(ch)
	v.MoveCursor(w, 0, true)
}

Then you will find everything is OK.

@zzqluvmyj
Copy link

zzqluvmyj commented Feb 1, 2022

It works, but in _examples/bufs.go , the output string contains space, I modify file view.go , change " " to ""

func (v *View) Buffer() string {
	str := ""
	for _, l := range v.lines {
		str += lineType(l).String() + "\n"
	}
	return strings.Replace(str, "\x00", "", -1)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants