From 21b81ccb2716d73cde4eda805cf1f5ea1642412e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 03:22:51 +0300 Subject: Initial changes to do #91 --- ui/messages/htmlmessage.go | 187 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 ui/messages/htmlmessage.go (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go new file mode 100644 index 0000000..de4b30c --- /dev/null +++ b/ui/messages/htmlmessage.go @@ -0,0 +1,187 @@ +// gomuks - A terminal Matrix client written in Go. +// Copyright (C) 2019 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package messages + +import ( + "time" + + "github.com/mattn/go-runewidth" + + "maunium.net/go/gomuks/config" + "maunium.net/go/gomuks/ui/widget" + "maunium.net/go/mautrix" + "maunium.net/go/mauview" + "maunium.net/go/tcell" +) + +type HTMLMessage struct { + BaseMessage + + Root *HTMLEntity +} + +func NewHTMLMessage(id, sender, displayname string, msgtype mautrix.MessageType, root *HTMLEntity, timestamp time.Time) UIMessage { + return &HTMLMessage{ + BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), + Root: root, + } +} +func (hw *HTMLMessage) Draw(screen mauview.Screen) { + hw.Root.Draw(screen) +} + +func (hw *HTMLMessage) OnKeyEvent(event mauview.KeyEvent) bool { + return false +} + +func (hw *HTMLMessage) OnMouseEvent(event mauview.MouseEvent) bool { + return false +} + +func (hw *HTMLMessage) OnPasteEvent(event mauview.PasteEvent) bool { + return false +} + +func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width int) { + // TODO account for bare messages in initial startX + startX := 0 + hw.Root.calculateBuffer(width, startX, preferences.BareMessageView) +} + +func (hw *HTMLMessage) Height() int { + return hw.Root.height +} + +func (hw *HTMLMessage) PlainText() string { + return "Plaintext unavailable" +} + +func (hw *HTMLMessage) NotificationContent() string { + return "Notification content unavailable" +} + +type HTMLEntity struct { + // Permanent variables + Tag string + Text string + Style tcell.Style + Children []*HTMLEntity + Block bool + Indent int + + // Non-permanent variables (calculated buffer data) + buffer []string + prevWidth int + startX int + height int +} + +func (he *HTMLEntity) AdjustStyle(fn func(tcell.Style) tcell.Style) *HTMLEntity { + for _, child := range he.Children { + child.AdjustStyle(fn) + } + he.Style = fn(he.Style) + return he +} + +func (he *HTMLEntity) Draw(screen mauview.Screen) { + width, _ := screen.Size() + if len(he.buffer) > 0 { + x := he.startX + for y, line := range he.buffer { + widget.WriteLine(screen, mauview.AlignLeft, line, x, y, width, he.Style) + x = 0 + } + } + if len(he.Children) > 0 { + proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} + for _, entity := range he.Children { + if entity.Block { + proxyScreen.OffsetY++ + } + proxyScreen.Height = entity.height + entity.Draw(proxyScreen) + proxyScreen.OffsetY += entity.height - 1 + } + } +} + +func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { + if len(he.Children) > 0 { + childStartX := 0 + for _, entity := range he.Children { + childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) + he.height += entity.height - 1 + } + } + if len(he.Text) > 0 && width != he.prevWidth { + he.prevWidth = width + he.buffer = make([]string, 0, 1) + text := he.Text + if !he.Block { + he.startX = startX + } else { + startX = 0 + } + for { + extract := runewidth.Truncate(text, width-startX, "") + extract = trim(extract, text, bare) + he.buffer = append(he.buffer, extract) + text = text[len(extract):] + startX = 0 + if len(text) == 0 { + he.height += len(he.buffer) + // This entity is over, return the startX for the next entity + if he.Block { + // ...except if it's a block entity + return 0 + } + return runewidth.StringWidth(extract) + } + } + } + return 0 +} + +// Regular expressions used to split lines when calculating the buffer. +/*var ( + boundaryPattern = regexp.MustCompile(`([[:punct:]]\s*|\s+)`) + bareBoundaryPattern = regexp.MustCompile(`(\s+)`) + spacePattern = regexp.MustCompile(`\s+`) +)*/ + +func trim(extract, full string, bare bool) string { + if len(extract) == len(full) { + return extract + } + if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 { + extract = full[:len(extract)+spaces[1]] + } + regex := boundaryPattern + if bare { + regex = bareBoundaryPattern + } + matches := regex.FindAllStringIndex(extract, -1) + if len(matches) > 0 { + if match := matches[len(matches)-1]; len(match) >= 2 { + if until := match[1]; until < len(extract) { + extract = extract[:until] + } + } + } + return extract +} -- cgit v1.2.3 From b0c4ef81e9a1c7d9376685875d795ad3b5d8db01 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 18:21:38 +0300 Subject: More changes to do #91 --- ui/messages/htmlmessage.go | 93 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 18 deletions(-) (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index de4b30c..33e4d87 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -17,6 +17,8 @@ package messages import ( + "fmt" + "strings" "time" "github.com/mattn/go-runewidth" @@ -57,9 +59,13 @@ func (hw *HTMLMessage) OnPasteEvent(event mauview.PasteEvent) bool { } func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width int) { + if width <= 0 { + panic("Negative width in CalculateBuffer") + } // TODO account for bare messages in initial startX startX := 0 hw.Root.calculateBuffer(width, startX, preferences.BareMessageView) + //debug.Print(hw.Root.String()) } func (hw *HTMLMessage) Height() int { @@ -109,8 +115,8 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { } if len(he.Children) > 0 { proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} - for _, entity := range he.Children { - if entity.Block { + for i, entity := range he.Children { + if i != 0 && entity.startX == 0 { proxyScreen.OffsetY++ } proxyScreen.Height = entity.height @@ -120,38 +126,87 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { } } +func (he *HTMLEntity) String() string { + var buf strings.Builder + buf.WriteString("&HTMLEntity{\n") + _, _ = fmt.Fprintf(&buf, ` Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,\n`, + he.Tag, he.Style, he.Block, he.Indent, he.startX, he.height) + _, _ = fmt.Fprintf(&buf, ` Buffer=["%s"]`, strings.Join(he.buffer, "\", \"")) + if len(he.Text) > 0 { + buf.WriteString(",\n") + _, _ = fmt.Fprintf(&buf, ` Text="%s"`, he.Text) + } + if len(he.Children) > 0 { + buf.WriteString(",\n") + buf.WriteString(" Children={") + for _, child := range he.Children { + buf.WriteString("\n ") + buf.WriteString(strings.Join(strings.Split(strings.TrimRight(child.String(), "\n"), "\n"), "\n ")) + } + buf.WriteString("\n },") + } + buf.WriteString("\n},\n") + return buf.String() +} + func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { + he.startX = startX + if he.Block { + he.startX = 0 + } + he.height = 0 if len(he.Children) > 0 { - childStartX := 0 + childStartX := he.startX for _, entity := range he.Children { + if entity.Block || childStartX == 0 || he.height == 0 { + he.height++ + } childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) he.height += entity.height - 1 } + if len(he.Text) == 0 && !he.Block { + return childStartX + } } - if len(he.Text) > 0 && width != he.prevWidth { + if len(he.Text) > 0 { he.prevWidth = width - he.buffer = make([]string, 0, 1) - text := he.Text - if !he.Block { - he.startX = startX - } else { - startX = 0 + if he.buffer == nil { + he.buffer = []string{} } + bufPtr := 0 + text := he.Text + textStartX := he.startX for { - extract := runewidth.Truncate(text, width-startX, "") - extract = trim(extract, text, bare) - he.buffer = append(he.buffer, extract) + extract := runewidth.Truncate(text, width-textStartX, "") + extract, wordWrapped := trim(extract, text, bare) + if !wordWrapped && textStartX > 0 { + if bufPtr < len(he.buffer) { + he.buffer[bufPtr] = "" + } else { + he.buffer = append(he.buffer, "") + } + bufPtr++ + textStartX = 0 + continue + } + if bufPtr < len(he.buffer) { + he.buffer[bufPtr] = extract + } else { + he.buffer = append(he.buffer, extract) + } + bufPtr++ text = text[len(extract):] - startX = 0 if len(text) == 0 { + he.buffer = he.buffer[:bufPtr] he.height += len(he.buffer) // This entity is over, return the startX for the next entity if he.Block { // ...except if it's a block entity return 0 } - return runewidth.StringWidth(extract) + return textStartX + runewidth.StringWidth(extract) } + textStartX = 0 } } return 0 @@ -164,12 +219,13 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { spacePattern = regexp.MustCompile(`\s+`) )*/ -func trim(extract, full string, bare bool) string { +func trim(extract, full string, bare bool) (string, bool) { if len(extract) == len(full) { - return extract + return extract, true } if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 { extract = full[:len(extract)+spaces[1]] + //return extract, true } regex := boundaryPattern if bare { @@ -180,8 +236,9 @@ func trim(extract, full string, bare bool) string { if match := matches[len(matches)-1]; len(match) >= 2 { if until := match[1]; until < len(extract) { extract = extract[:until] + return extract, true } } } - return extract + return extract, len(extract) > 0 && extract[len(extract)-1] == ' ' } -- cgit v1.2.3 From 083ae8bd44ad34859781ea88cae9f57d9404c7e5 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 18:25:13 +0300 Subject: Remove commented code --- ui/messages/htmlmessage.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index 33e4d87..51678ba 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -65,7 +65,6 @@ func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width // TODO account for bare messages in initial startX startX := 0 hw.Root.calculateBuffer(width, startX, preferences.BareMessageView) - //debug.Print(hw.Root.String()) } func (hw *HTMLMessage) Height() int { @@ -73,10 +72,12 @@ func (hw *HTMLMessage) Height() int { } func (hw *HTMLMessage) PlainText() string { + // FIXME return "Plaintext unavailable" } func (hw *HTMLMessage) NotificationContent() string { + // FIXME return "Notification content unavailable" } @@ -212,20 +213,12 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { return 0 } -// Regular expressions used to split lines when calculating the buffer. -/*var ( - boundaryPattern = regexp.MustCompile(`([[:punct:]]\s*|\s+)`) - bareBoundaryPattern = regexp.MustCompile(`(\s+)`) - spacePattern = regexp.MustCompile(`\s+`) -)*/ - func trim(extract, full string, bare bool) (string, bool) { if len(extract) == len(full) { return extract, true } if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 { extract = full[:len(extract)+spaces[1]] - //return extract, true } regex := boundaryPattern if bare { -- cgit v1.2.3 From cf93671ecdebc96cfd45fefaeb9fc95f62393a33 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 20:13:23 +0300 Subject: Add syntax highlighting. Fixes #28 --- ui/messages/htmlmessage.go | 50 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index 51678ba..0ffb6c9 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -72,23 +72,23 @@ func (hw *HTMLMessage) Height() int { } func (hw *HTMLMessage) PlainText() string { - // FIXME - return "Plaintext unavailable" + return hw.Root.PlainText() } func (hw *HTMLMessage) NotificationContent() string { - // FIXME - return "Notification content unavailable" + return hw.Root.PlainText() } type HTMLEntity struct { // Permanent variables - Tag string - Text string - Style tcell.Style - Children []*HTMLEntity - Block bool - Indent int + Tag string + Text string + Style tcell.Style + Children []*HTMLEntity + Block bool + Indent int + + DefaultHeight int // Non-permanent variables (calculated buffer data) buffer []string @@ -130,8 +130,9 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { func (he *HTMLEntity) String() string { var buf strings.Builder buf.WriteString("&HTMLEntity{\n") - _, _ = fmt.Fprintf(&buf, ` Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,\n`, + _, _ = fmt.Fprintf(&buf, ` Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,`, he.Tag, he.Style, he.Block, he.Indent, he.startX, he.height) + buf.WriteRune('\n') _, _ = fmt.Fprintf(&buf, ` Buffer=["%s"]`, strings.Join(he.buffer, "\", \"")) if len(he.Text) > 0 { buf.WriteString(",\n") @@ -150,6 +151,27 @@ func (he *HTMLEntity) String() string { return buf.String() } +func (he *HTMLEntity) PlainText() string { + if len(he.Children) == 0 { + return he.Text + } + var buf strings.Builder + buf.WriteString(he.Text) + newlined := false + for _, child := range he.Children { + if child.Block && !newlined { + buf.WriteRune('\n') + } + newlined = false + buf.WriteString(child.PlainText()) + if child.Block { + buf.WriteRune('\n') + newlined = true + } + } + return buf.String() +} + func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { he.startX = startX if he.Block { @@ -178,6 +200,7 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { text := he.Text textStartX := he.startX for { + // TODO add option no wrap and character wrap options extract := runewidth.Truncate(text, width-textStartX, "") extract, wordWrapped := trim(extract, text, bare) if !wordWrapped && textStartX > 0 { @@ -210,7 +233,10 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { textStartX = 0 } } - return 0 + if len(he.Text) == 0 && len(he.Children) == 0 { + he.height = he.DefaultHeight + } + return he.startX } func trim(extract, full string, bare bool) (string, bool) { -- cgit v1.2.3 From e6180c9b6fb12686926cf8d3bc27f31f6489129b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 22:25:53 +0300 Subject: Move special list/blockquote prefixing to renderer --- ui/messages/htmlmessage.go | 175 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 25 deletions(-) (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index 0ffb6c9..34bba2b 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -18,25 +18,27 @@ package messages import ( "fmt" + "math" "strings" "time" "github.com/mattn/go-runewidth" - "maunium.net/go/gomuks/config" - "maunium.net/go/gomuks/ui/widget" "maunium.net/go/mautrix" "maunium.net/go/mauview" "maunium.net/go/tcell" + + "maunium.net/go/gomuks/config" + "maunium.net/go/gomuks/ui/widget" ) type HTMLMessage struct { BaseMessage - Root *HTMLEntity + Root HTMLEntity } -func NewHTMLMessage(id, sender, displayname string, msgtype mautrix.MessageType, root *HTMLEntity, timestamp time.Time) UIMessage { +func NewHTMLMessage(id, sender, displayname string, msgtype mautrix.MessageType, root HTMLEntity, timestamp time.Time) UIMessage { return &HTMLMessage{ BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), Root: root, @@ -68,7 +70,7 @@ func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width } func (hw *HTMLMessage) Height() int { - return hw.Root.height + return hw.Root.Height() } func (hw *HTMLMessage) PlainText() string { @@ -79,14 +81,106 @@ func (hw *HTMLMessage) NotificationContent() string { return hw.Root.PlainText() } -type HTMLEntity struct { +type AdjustStyleFunc func(tcell.Style) tcell.Style + +type HTMLEntity interface { + AdjustStyle(AdjustStyleFunc) HTMLEntity + Draw(screen mauview.Screen) + IsBlock() bool + GetTag() string + PlainText() string + String() string + Height() int + + calculateBuffer(width, startX int, bare bool) int + getStartX() int +} + +type BlockquoteEntity struct { + *BaseHTMLEntity +} + +func NewBlockquoteEntity(children []HTMLEntity) *BlockquoteEntity { + return &BlockquoteEntity{&BaseHTMLEntity{ + Tag: "blockquote", + Children: children, + Block: true, + Indent: 2, + }} +} + +func (be *BlockquoteEntity) Draw(screen mauview.Screen) { + be.BaseHTMLEntity.Draw(screen) + for y := 0; y < be.height; y++ { + screen.SetContent(0, y, '>', nil, be.Style) + } +} + +func (be *BlockquoteEntity) String() string { + return fmt.Sprintf("&BlockquoteEntity{%s},\n", be.BaseHTMLEntity) +} + +type ListEntity struct { + *BaseHTMLEntity + Ordered bool + Start int +} + +func digits(num int) int { + if num <= 0 { + return 0 + } + return int(math.Floor(math.Log10(float64(num))) + 1) +} + +func NewListEntity(ordered bool, start int, children []HTMLEntity) *ListEntity { + entity := &ListEntity{ + BaseHTMLEntity: &BaseHTMLEntity{ + Tag: "ul", + Children: children, + Block: true, + Indent: 2, + }, + Ordered: ordered, + Start: start, + } + if ordered { + entity.Tag = "ol" + entity.Indent += digits(start + len(children) - 1) + } + return entity +} + +func (le *ListEntity) Draw(screen mauview.Screen) { + width, _ := screen.Size() + + proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: le.Indent, Width: width - le.Indent} + for i, entity := range le.Children { + proxyScreen.Height = entity.Height() + if le.Ordered { + number := le.Start + i + line := fmt.Sprintf("%d. %s", number, strings.Repeat(" ", le.Indent-2-digits(number))) + widget.WriteLine(screen, mauview.AlignLeft, line, 0, proxyScreen.OffsetY, le.Indent, le.Style) + } else { + screen.SetContent(0, proxyScreen.OffsetY, '●', nil, le.Style) + } + entity.Draw(proxyScreen) + proxyScreen.OffsetY += entity.Height() + } +} + +func (le *ListEntity) String() string { + return fmt.Sprintf("&ListEntity{Ordered=%t, Start=%d, Base=%s},\n", le.Ordered, le.Start, le.BaseHTMLEntity) +} + +type BaseHTMLEntity struct { // Permanent variables - Tag string - Text string - Style tcell.Style - Children []*HTMLEntity - Block bool - Indent int + Tag string + Text string + Style tcell.Style + Children []HTMLEntity + Block bool + Indent int DefaultHeight int @@ -97,7 +191,22 @@ type HTMLEntity struct { height int } -func (he *HTMLEntity) AdjustStyle(fn func(tcell.Style) tcell.Style) *HTMLEntity { +func NewHTMLTextEntity(text string) *BaseHTMLEntity { + return &BaseHTMLEntity{ + Tag: "text", + Text: text, + } +} + +func NewHTMLEntity(tag string, children []HTMLEntity, block bool) *BaseHTMLEntity { + return &BaseHTMLEntity{ + Tag: tag, + Children: children, + Block: block, + } +} + +func (he *BaseHTMLEntity) AdjustStyle(fn AdjustStyleFunc) HTMLEntity { for _, child := range he.Children { child.AdjustStyle(fn) } @@ -105,7 +214,23 @@ func (he *HTMLEntity) AdjustStyle(fn func(tcell.Style) tcell.Style) *HTMLEntity return he } -func (he *HTMLEntity) Draw(screen mauview.Screen) { +func (he *BaseHTMLEntity) IsBlock() bool { + return he.Block +} + +func (he *BaseHTMLEntity) GetTag() string { + return he.Tag +} + +func (he *BaseHTMLEntity) Height() int { + return he.height +} + +func (he *BaseHTMLEntity) getStartX() int { + return he.startX +} + +func (he *BaseHTMLEntity) Draw(screen mauview.Screen) { width, _ := screen.Size() if len(he.buffer) > 0 { x := he.startX @@ -117,19 +242,19 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { if len(he.Children) > 0 { proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} for i, entity := range he.Children { - if i != 0 && entity.startX == 0 { + if i != 0 && entity.getStartX() == 0 { proxyScreen.OffsetY++ } - proxyScreen.Height = entity.height + proxyScreen.Height = entity.Height() entity.Draw(proxyScreen) - proxyScreen.OffsetY += entity.height - 1 + proxyScreen.OffsetY += entity.Height() - 1 } } } -func (he *HTMLEntity) String() string { +func (he *BaseHTMLEntity) String() string { var buf strings.Builder - buf.WriteString("&HTMLEntity{\n") + buf.WriteString("&BaseHTMLEntity{\n") _, _ = fmt.Fprintf(&buf, ` Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,`, he.Tag, he.Style, he.Block, he.Indent, he.startX, he.height) buf.WriteRune('\n') @@ -151,7 +276,7 @@ func (he *HTMLEntity) String() string { return buf.String() } -func (he *HTMLEntity) PlainText() string { +func (he *BaseHTMLEntity) PlainText() string { if len(he.Children) == 0 { return he.Text } @@ -159,12 +284,12 @@ func (he *HTMLEntity) PlainText() string { buf.WriteString(he.Text) newlined := false for _, child := range he.Children { - if child.Block && !newlined { + if child.IsBlock() && !newlined { buf.WriteRune('\n') } newlined = false buf.WriteString(child.PlainText()) - if child.Block { + if child.IsBlock() { buf.WriteRune('\n') newlined = true } @@ -172,7 +297,7 @@ func (he *HTMLEntity) PlainText() string { return buf.String() } -func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { +func (he *BaseHTMLEntity) calculateBuffer(width, startX int, bare bool) int { he.startX = startX if he.Block { he.startX = 0 @@ -181,11 +306,11 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { if len(he.Children) > 0 { childStartX := he.startX for _, entity := range he.Children { - if entity.Block || childStartX == 0 || he.height == 0 { + if entity.IsBlock() || childStartX == 0 || he.height == 0 { he.height++ } childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) - he.height += entity.height - 1 + he.height += entity.Height() - 1 } if len(he.Text) == 0 && !he.Block { return childStartX -- cgit v1.2.3 From 5d7c1a4caab46f7e981aed7b9cc825b7602b4098 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 22:54:55 +0300 Subject: Improve handling of multiple linebreaks --- ui/messages/htmlmessage.go | 67 +++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 22 deletions(-) (limited to 'ui/messages/htmlmessage.go') diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index 34bba2b..8685442 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -173,6 +173,17 @@ func (le *ListEntity) String() string { return fmt.Sprintf("&ListEntity{Ordered=%t, Start=%d, Base=%s},\n", le.Ordered, le.Start, le.BaseHTMLEntity) } +type BreakEntity struct { + *BaseHTMLEntity +} + +func NewBreakEntity() *BreakEntity { + return &BreakEntity{&BaseHTMLEntity{ + Tag: "br", + Block: true, + }} +} + type BaseHTMLEntity struct { // Permanent variables Tag string @@ -230,28 +241,6 @@ func (he *BaseHTMLEntity) getStartX() int { return he.startX } -func (he *BaseHTMLEntity) Draw(screen mauview.Screen) { - width, _ := screen.Size() - if len(he.buffer) > 0 { - x := he.startX - for y, line := range he.buffer { - widget.WriteLine(screen, mauview.AlignLeft, line, x, y, width, he.Style) - x = 0 - } - } - if len(he.Children) > 0 { - proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} - for i, entity := range he.Children { - if i != 0 && entity.getStartX() == 0 { - proxyScreen.OffsetY++ - } - proxyScreen.Height = entity.Height() - entity.Draw(proxyScreen) - proxyScreen.OffsetY += entity.Height() - 1 - } - } -} - func (he *BaseHTMLEntity) String() string { var buf strings.Builder buf.WriteString("&BaseHTMLEntity{\n") @@ -297,6 +286,34 @@ func (he *BaseHTMLEntity) PlainText() string { return buf.String() } +func (he *BaseHTMLEntity) Draw(screen mauview.Screen) { + width, _ := screen.Size() + if len(he.buffer) > 0 { + x := he.startX + for y, line := range he.buffer { + widget.WriteLine(screen, mauview.AlignLeft, line, x, y, width, he.Style) + x = 0 + } + } + if len(he.Children) > 0 { + prevBreak := false + proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} + for i, entity := range he.Children { + if i != 0 && entity.getStartX() == 0 { + proxyScreen.OffsetY++ + } + proxyScreen.Height = entity.Height() + entity.Draw(proxyScreen) + proxyScreen.OffsetY += entity.Height() - 1 + _, isBreak := entity.(*BreakEntity) + if prevBreak && isBreak { + proxyScreen.OffsetY++ + } + prevBreak = isBreak + } + } +} + func (he *BaseHTMLEntity) calculateBuffer(width, startX int, bare bool) int { he.startX = startX if he.Block { @@ -305,12 +322,18 @@ func (he *BaseHTMLEntity) calculateBuffer(width, startX int, bare bool) int { he.height = 0 if len(he.Children) > 0 { childStartX := he.startX + prevBreak := false for _, entity := range he.Children { if entity.IsBlock() || childStartX == 0 || he.height == 0 { he.height++ } childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) he.height += entity.Height() - 1 + _, isBreak := entity.(*BreakEntity) + if prevBreak && isBreak { + he.height++ + } + prevBreak = isBreak } if len(he.Text) == 0 && !he.Block { return childStartX -- cgit v1.2.3