Add support for rendering spoilers. Fixes #331

This commit is contained in:
Tulir Asokan
2022-04-15 15:14:18 +03:00
parent 751a158fbf
commit a5bdba204e
18 changed files with 212 additions and 81 deletions
+2
View File
@@ -10,6 +10,8 @@
(thanks to [@tleb] in [#354]). (thanks to [@tleb] in [#354]).
* Added tab-completion support for `/toggle` options * Added tab-completion support for `/toggle` options
(thanks to [@n-peugnet] in [#362]). (thanks to [@n-peugnet] in [#362]).
* Added initial support for rendering spoilers in messages.
* Fixed mentions being lost when editing messages.
* Fixed date change messages showing the wrong date. * Fixed date change messages showing the wrong date.
* Fixed some whitespace in HTML being rendered even when it shouldn't. * Fixed some whitespace in HTML being rendered even when it shouldn't.
+2 -2
View File
@@ -34,7 +34,7 @@ import (
) )
type MessageRenderer interface { type MessageRenderer interface {
Draw(screen mauview.Screen) Draw(screen mauview.Screen, msg *UIMessage)
NotificationContent() string NotificationContent() string
PlainText() string PlainText() string
CalculateBuffer(prefs config.UserPreferences, width int, msg *UIMessage) CalculateBuffer(prefs config.UserPreferences, width int, msg *UIMessage)
@@ -324,7 +324,7 @@ func (msg *UIMessage) DrawReactions(screen mauview.Screen) {
func (msg *UIMessage) Draw(screen mauview.Screen) { func (msg *UIMessage) Draw(screen mauview.Screen) {
proxyScreen := msg.DrawReply(screen) proxyScreen := msg.DrawReply(screen)
msg.Renderer.Draw(proxyScreen) msg.Renderer.Draw(proxyScreen, msg)
msg.DrawReactions(proxyScreen) msg.DrawReactions(proxyScreen)
if msg.IsSelected { if msg.IsSelected {
w, h := screen.Size() w, h := screen.Size()
+1 -1
View File
@@ -83,7 +83,7 @@ func (msg *ExpandedTextMessage) Height() int {
return len(msg.buffer) return len(msg.buffer)
} }
func (msg *ExpandedTextMessage) Draw(screen mauview.Screen) { func (msg *ExpandedTextMessage) Draw(screen mauview.Screen, _ *UIMessage) {
for y, line := range msg.buffer { for y, line := range msg.buffer {
line.Draw(screen, 0, y) line.Draw(screen, 0, y)
} }
+1 -1
View File
@@ -170,7 +170,7 @@ func (msg *FileMessage) Height() int {
return len(msg.buffer) return len(msg.buffer)
} }
func (msg *FileMessage) Draw(screen mauview.Screen) { func (msg *FileMessage) Draw(screen mauview.Screen, _ *UIMessage) {
for y, line := range msg.buffer { for y, line := range msg.buffer {
line.Draw(screen, 0, y) line.Draw(screen, 0, y)
} }
+3 -3
View File
@@ -38,7 +38,7 @@ type BaseEntity struct {
} }
// AdjustStyle changes the style of this text entity. // AdjustStyle changes the style of this text entity.
func (be *BaseEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (be *BaseEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
be.Style = fn(be.Style) be.Style = fn(be.Style)
return be return be
} }
@@ -87,7 +87,7 @@ func (be *BaseEntity) String() string {
} }
// CalculateBuffer prepares this entity for rendering with the given parameters. // CalculateBuffer prepares this entity for rendering with the given parameters.
func (be *BaseEntity) CalculateBuffer(width, startX int, bare bool) int { func (be *BaseEntity) CalculateBuffer(width, startX int, ctx DrawContext) int {
be.height = be.DefaultHeight be.height = be.DefaultHeight
be.startX = startX be.startX = startX
if be.Block { if be.Block {
@@ -96,6 +96,6 @@ func (be *BaseEntity) CalculateBuffer(width, startX int, bare bool) int {
return be.startX return be.startX
} }
func (be *BaseEntity) Draw(screen mauview.Screen) { func (be *BaseEntity) Draw(screen mauview.Screen, ctx DrawContext) {
panic("Called Draw() of BaseEntity") panic("Called Draw() of BaseEntity")
} }
+4 -4
View File
@@ -40,8 +40,8 @@ func NewBlockquoteEntity(children []Entity) *BlockquoteEntity {
}} }}
} }
func (be *BlockquoteEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (be *BlockquoteEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
be.BaseEntity = be.BaseEntity.AdjustStyle(fn).(*BaseEntity) be.BaseEntity = be.BaseEntity.AdjustStyle(fn, reason).(*BaseEntity)
return be return be
} }
@@ -49,8 +49,8 @@ func (be *BlockquoteEntity) Clone() Entity {
return &BlockquoteEntity{ContainerEntity: be.ContainerEntity.Clone().(*ContainerEntity)} return &BlockquoteEntity{ContainerEntity: be.ContainerEntity.Clone().(*ContainerEntity)}
} }
func (be *BlockquoteEntity) Draw(screen mauview.Screen) { func (be *BlockquoteEntity) Draw(screen mauview.Screen, ctx DrawContext) {
be.ContainerEntity.Draw(screen) be.ContainerEntity.Draw(screen, ctx)
for y := 0; y < be.height; y++ { for y := 0; y < be.height; y++ {
screen.SetContent(0, y, BlockQuoteChar, nil, be.Style) screen.SetContent(0, y, BlockQuoteChar, nil, be.Style)
} }
+3 -3
View File
@@ -32,8 +32,8 @@ func NewBreakEntity() *BreakEntity {
} }
// AdjustStyle changes the style of this text entity. // AdjustStyle changes the style of this text entity.
func (be *BreakEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (be *BreakEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
be.BaseEntity = be.BaseEntity.AdjustStyle(fn).(*BaseEntity) be.BaseEntity = be.BaseEntity.AdjustStyle(fn, reason).(*BaseEntity)
return be return be
} }
@@ -49,6 +49,6 @@ func (be *BreakEntity) String() string {
return "&html.BreakEntity{},\n" return "&html.BreakEntity{},\n"
} }
func (be *BreakEntity) Draw(screen mauview.Screen) { func (be *BreakEntity) Draw(screen mauview.Screen, ctx DrawContext) {
// No-op, the logic happens in containers // No-op, the logic happens in containers
} }
+6 -4
View File
@@ -46,12 +46,14 @@ func (ce *CodeBlockEntity) Clone() Entity {
} }
} }
func (ce *CodeBlockEntity) Draw(screen mauview.Screen) { func (ce *CodeBlockEntity) Draw(screen mauview.Screen, ctx DrawContext) {
screen.Fill(' ', ce.Background) screen.Fill(' ', ce.Background)
ce.ContainerEntity.Draw(screen) ce.ContainerEntity.Draw(screen, ctx)
} }
func (ce *CodeBlockEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (ce *CodeBlockEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
// Don't allow adjusting code block style. if reason != AdjustStyleReasonNormal {
ce.ContainerEntity.AdjustStyle(fn, reason)
}
return ce return ce
} }
+8 -8
View File
@@ -61,15 +61,15 @@ func (ce *ContainerEntity) PlainText() string {
} }
// AdjustStyle recursively changes the style of this entity and all its children. // AdjustStyle recursively changes the style of this entity and all its children.
func (ce *ContainerEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (ce *ContainerEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
for _, child := range ce.Children { for _, child := range ce.Children {
child.AdjustStyle(fn) child.AdjustStyle(fn, reason)
} }
ce.Style = fn(ce.Style) ce.Style = fn(ce.Style)
return ce return ce
} }
// clone creates a deep copy of this base entity. // Clone creates a deep copy of this base entity.
func (ce *ContainerEntity) Clone() Entity { func (ce *ContainerEntity) Clone() Entity {
children := make([]Entity, len(ce.Children)) children := make([]Entity, len(ce.Children))
for i, child := range ce.Children { for i, child := range ce.Children {
@@ -98,7 +98,7 @@ func (ce *ContainerEntity) String() string {
} }
// Draw draws this entity onto the given mauview Screen. // Draw draws this entity onto the given mauview Screen.
func (ce *ContainerEntity) Draw(screen mauview.Screen) { func (ce *ContainerEntity) Draw(screen mauview.Screen, ctx DrawContext) {
if len(ce.Children) == 0 { if len(ce.Children) == 0 {
return return
} }
@@ -110,7 +110,7 @@ func (ce *ContainerEntity) Draw(screen mauview.Screen) {
proxyScreen.OffsetY++ proxyScreen.OffsetY++
} }
proxyScreen.Height = entity.Height() proxyScreen.Height = entity.Height()
entity.Draw(proxyScreen) entity.Draw(proxyScreen, ctx)
proxyScreen.SetStyle(ce.Style) proxyScreen.SetStyle(ce.Style)
proxyScreen.OffsetY += entity.Height() - 1 proxyScreen.OffsetY += entity.Height() - 1
_, isBreak := entity.(*BreakEntity) _, isBreak := entity.(*BreakEntity)
@@ -122,8 +122,8 @@ func (ce *ContainerEntity) Draw(screen mauview.Screen) {
} }
// CalculateBuffer prepares this entity and all its children for rendering with the given parameters // CalculateBuffer prepares this entity and all its children for rendering with the given parameters
func (ce *ContainerEntity) CalculateBuffer(width, startX int, bare bool) int { func (ce *ContainerEntity) CalculateBuffer(width, startX int, ctx DrawContext) int {
ce.BaseEntity.CalculateBuffer(width, startX, bare) ce.BaseEntity.CalculateBuffer(width, startX, ctx)
if len(ce.Children) > 0 { if len(ce.Children) > 0 {
ce.height = 0 ce.height = 0
childStartX := ce.startX childStartX := ce.startX
@@ -132,7 +132,7 @@ func (ce *ContainerEntity) CalculateBuffer(width, startX int, bare bool) int {
if entity.IsBlock() || childStartX == 0 || ce.height == 0 { if entity.IsBlock() || childStartX == 0 || ce.height == 0 {
ce.height++ ce.height++
} }
childStartX = entity.CalculateBuffer(width-ce.Indent, childStartX, bare) childStartX = entity.CalculateBuffer(width-ce.Indent, childStartX, ctx)
ce.height += entity.Height() - 1 ce.height += entity.Height() - 1
_, isBreak := entity.(*BreakEntity) _, isBreak := entity.(*BreakEntity)
if prevBreak && isBreak { if prevBreak && isBreak {
+15 -3
View File
@@ -24,11 +24,23 @@ import (
// AdjustStyleFunc is a lambda function type to edit an existing tcell Style. // AdjustStyleFunc is a lambda function type to edit an existing tcell Style.
type AdjustStyleFunc func(tcell.Style) tcell.Style type AdjustStyleFunc func(tcell.Style) tcell.Style
type AdjustStyleReason int
const (
AdjustStyleReasonNormal AdjustStyleReason = iota
AdjustStyleReasonHideSpoiler
)
type DrawContext struct {
IsSelected bool
BareMessages bool
}
type Entity interface { type Entity interface {
// AdjustStyle recursively changes the style of the entity and all its children. // AdjustStyle recursively changes the style of the entity and all its children.
AdjustStyle(AdjustStyleFunc) Entity AdjustStyle(AdjustStyleFunc, AdjustStyleReason) Entity
// Draw draws the entity onto the given mauview Screen. // Draw draws the entity onto the given mauview Screen.
Draw(screen mauview.Screen) Draw(screen mauview.Screen, ctx DrawContext)
// IsBlock returns whether or not it's a block-type entity. // IsBlock returns whether or not it's a block-type entity.
IsBlock() bool IsBlock() bool
// GetTag returns the HTML tag of the entity. // GetTag returns the HTML tag of the entity.
@@ -43,7 +55,7 @@ type Entity interface {
// Height returns the render height of the entity. // Height returns the render height of the entity.
Height() int Height() int
// CalculateBuffer prepares the entity and all its children for rendering with the given parameters // CalculateBuffer prepares the entity and all its children for rendering with the given parameters
CalculateBuffer(width, startX int, bare bool) int CalculateBuffer(width, startX int, ctx DrawContext) int
getStartX() int getStartX() int
+3 -3
View File
@@ -36,8 +36,8 @@ func NewHorizontalLineEntity() *HorizontalLineEntity {
}} }}
} }
func (he *HorizontalLineEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (he *HorizontalLineEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
he.BaseEntity = he.BaseEntity.AdjustStyle(fn).(*BaseEntity) he.BaseEntity = he.BaseEntity.AdjustStyle(fn, reason).(*BaseEntity)
return he return he
} }
@@ -45,7 +45,7 @@ func (he *HorizontalLineEntity) Clone() Entity {
return NewHorizontalLineEntity() return NewHorizontalLineEntity()
} }
func (he *HorizontalLineEntity) Draw(screen mauview.Screen) { func (he *HorizontalLineEntity) Draw(screen mauview.Screen, ctx DrawContext) {
width, _ := screen.Size() width, _ := screen.Size()
for x := 0; x < width; x++ { for x := 0; x < width; x++ {
screen.SetContent(x, 0, HorizontalLineChar, nil, he.Style) screen.SetContent(x, 0, HorizontalLineChar, nil, he.Style)
+5 -4
View File
@@ -59,8 +59,9 @@ func NewListEntity(ordered bool, start int, children []Entity) *ListEntity {
return entity return entity
} }
func (le *ListEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (le *ListEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
le.BaseEntity = le.BaseEntity.AdjustStyle(fn).(*BaseEntity) le.BaseEntity = le.BaseEntity.AdjustStyle(fn, reason).(*BaseEntity)
le.ContainerEntity.AdjustStyle(fn, reason)
return le return le
} }
@@ -72,7 +73,7 @@ func (le *ListEntity) Clone() Entity {
} }
} }
func (le *ListEntity) Draw(screen mauview.Screen) { func (le *ListEntity) Draw(screen mauview.Screen, ctx DrawContext) {
width, _ := screen.Size() width, _ := screen.Size()
proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: le.Indent, Width: width - le.Indent, Style: le.Style} proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: le.Indent, Width: width - le.Indent, Style: le.Style}
@@ -85,7 +86,7 @@ func (le *ListEntity) Draw(screen mauview.Screen) {
} else { } else {
screen.SetContent(0, proxyScreen.OffsetY, '●', nil, le.Style) screen.SetContent(0, proxyScreen.OffsetY, '●', nil, le.Style)
} }
entity.Draw(proxyScreen) entity.Draw(proxyScreen, ctx)
proxyScreen.SetStyle(le.Style) proxyScreen.SetStyle(le.Style)
proxyScreen.OffsetY += entity.Height() proxyScreen.OffsetY += entity.Height()
} }
+22 -17
View File
@@ -75,22 +75,23 @@ func AdjustStyleBackgroundColor(color tcell.Color) func(tcell.Style) tcell.Style
} }
} }
func (parser *htmlParser) getAttribute(node *html.Node, attribute string) string { func (parser *htmlParser) maybeGetAttribute(node *html.Node, attribute string) (string, bool) {
for _, attr := range node.Attr { for _, attr := range node.Attr {
if attr.Key == attribute { if attr.Key == attribute {
return attr.Val return attr.Val, true
} }
} }
return "" return "", false
}
func (parser *htmlParser) getAttribute(node *html.Node, attribute string) string {
val, _ := parser.maybeGetAttribute(node, attribute)
return val
} }
func (parser *htmlParser) hasAttribute(node *html.Node, attribute string) bool { func (parser *htmlParser) hasAttribute(node *html.Node, attribute string) bool {
for _, attr := range node.Attr { _, ok := parser.maybeGetAttribute(node, attribute)
if attr.Key == attribute { return ok
return true
}
}
return false
} }
func (parser *htmlParser) listToEntity(node *html.Node) Entity { func (parser *htmlParser) listToEntity(node *html.Node) Entity {
@@ -124,21 +125,25 @@ func (parser *htmlParser) basicFormatToEntity(node *html.Node) Entity {
} }
switch node.Data { switch node.Data {
case "b", "strong": case "b", "strong":
entity.AdjustStyle(AdjustStyleBold) entity.AdjustStyle(AdjustStyleBold, AdjustStyleReasonNormal)
case "i", "em": case "i", "em":
entity.AdjustStyle(AdjustStyleItalic) entity.AdjustStyle(AdjustStyleItalic, AdjustStyleReasonNormal)
case "s", "del", "strike": case "s", "del", "strike":
entity.AdjustStyle(AdjustStyleStrikethrough) entity.AdjustStyle(AdjustStyleStrikethrough, AdjustStyleReasonNormal)
case "u", "ins": case "u", "ins":
entity.AdjustStyle(AdjustStyleUnderline) entity.AdjustStyle(AdjustStyleUnderline, AdjustStyleReasonNormal)
case "font", "span": case "font", "span":
fgColor, ok := parser.parseColor(node, "data-mx-color", "color") fgColor, ok := parser.parseColor(node, "data-mx-color", "color")
if ok { if ok {
entity.AdjustStyle(AdjustStyleTextColor(fgColor)) entity.AdjustStyle(AdjustStyleTextColor(fgColor), AdjustStyleReasonNormal)
} }
bgColor, ok := parser.parseColor(node, "data-mx-bg-color", "background-color") bgColor, ok := parser.parseColor(node, "data-mx-bg-color", "background-color")
if ok { if ok {
entity.AdjustStyle(AdjustStyleBackgroundColor(bgColor)) entity.AdjustStyle(AdjustStyleBackgroundColor(bgColor), AdjustStyleReasonNormal)
}
spoilerReason, isSpoiler := parser.maybeGetAttribute(node, "data-mx-spoiler")
if isSpoiler {
return NewSpoilerEntity(entity, spoilerReason)
} }
} }
return entity return entity
@@ -175,7 +180,7 @@ func (parser *htmlParser) headerToEntity(node *html.Node) Entity {
[]Entity{NewTextEntity(strings.Repeat("#", int(node.Data[1]-'0')) + " ")}, []Entity{NewTextEntity(strings.Repeat("#", int(node.Data[1]-'0')) + " ")},
parser.nodeToEntities(node.FirstChild)..., parser.nodeToEntities(node.FirstChild)...,
), ),
}).AdjustStyle(AdjustStyleBold) }).AdjustStyle(AdjustStyleBold, AdjustStyleReasonNormal)
} }
func (parser *htmlParser) blockquoteToEntity(node *html.Node) Entity { func (parser *htmlParser) blockquoteToEntity(node *html.Node) Entity {
@@ -468,7 +473,7 @@ func Parse(prefs *config.UserPreferences, room *rooms.Room, content *event.Messa
}, },
Children: []Entity{ Children: []Entity{
NewTextEntity("* "), NewTextEntity("* "),
NewTextEntity(senderDisplayname).AdjustStyle(AdjustStyleTextColor(widget.GetHashColor(sender))), NewTextEntity(senderDisplayname).AdjustStyle(AdjustStyleTextColor(widget.GetHashColor(sender)), AdjustStyleReasonNormal),
NewTextEntity(" "), NewTextEntity(" "),
root, root,
}, },
+120
View File
@@ -0,0 +1,120 @@
// gomuks - A terminal Matrix client written in Go.
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
package html
import (
"fmt"
"strings"
"go.mau.fi/mauview"
"go.mau.fi/tcell"
)
type SpoilerEntity struct {
reason string
hidden *ContainerEntity
visible *ContainerEntity
}
const SpoilerColor = tcell.ColorYellow
func NewSpoilerEntity(visible *ContainerEntity, reason string) *SpoilerEntity {
hidden := visible.Clone().(*ContainerEntity)
hidden.AdjustStyle(func(style tcell.Style) tcell.Style {
return style.Foreground(SpoilerColor).Background(SpoilerColor)
}, AdjustStyleReasonHideSpoiler)
if len(reason) > 0 {
reasonEnt := NewTextEntity(fmt.Sprintf("(%s)", reason))
hidden.Children = append([]Entity{reasonEnt}, hidden.Children...)
visible.Children = append([]Entity{reasonEnt}, visible.Children...)
}
return &SpoilerEntity{
reason: reason,
hidden: hidden,
visible: visible,
}
}
func (se *SpoilerEntity) Clone() Entity {
return &SpoilerEntity{
reason: se.reason,
hidden: se.hidden.Clone().(*ContainerEntity),
visible: se.visible.Clone().(*ContainerEntity),
}
}
func (se *SpoilerEntity) IsBlock() bool {
return false
}
func (se *SpoilerEntity) GetTag() string {
return "span"
}
func (se *SpoilerEntity) Draw(screen mauview.Screen, ctx DrawContext) {
if ctx.IsSelected {
se.visible.Draw(screen, ctx)
} else {
se.hidden.Draw(screen, ctx)
}
}
func (se *SpoilerEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
if reason != AdjustStyleReasonHideSpoiler {
se.hidden.AdjustStyle(func(style tcell.Style) tcell.Style {
return fn(style).Foreground(SpoilerColor).Background(SpoilerColor)
}, reason)
se.visible.AdjustStyle(fn, reason)
}
return se
}
func (se *SpoilerEntity) PlainText() string {
if len(se.reason) > 0 {
return fmt.Sprintf("spoiler: %s", se.reason)
} else {
return "spoiler"
}
}
func (se *SpoilerEntity) String() string {
var buf strings.Builder
_, _ = fmt.Fprintf(&buf, `&html.SpoilerEntity{reason=%s`, se.reason)
buf.WriteString("\n visible=")
buf.WriteString(strings.Join(strings.Split(strings.TrimRight(se.visible.String(), "\n"), "\n"), "\n "))
buf.WriteString("\n hidden=")
buf.WriteString(strings.Join(strings.Split(strings.TrimRight(se.hidden.String(), "\n"), "\n"), "\n "))
buf.WriteString("\n]},")
return buf.String()
}
func (se *SpoilerEntity) Height() int {
return se.visible.Height()
}
func (se *SpoilerEntity) CalculateBuffer(width, startX int, ctx DrawContext) int {
se.hidden.CalculateBuffer(width, startX, ctx)
return se.visible.CalculateBuffer(width, startX, ctx)
}
func (se *SpoilerEntity) getStartX() int {
return se.visible.getStartX()
}
func (se *SpoilerEntity) IsEmpty() bool {
return se.visible.IsEmpty()
}
+6 -6
View File
@@ -49,8 +49,8 @@ func (te *TextEntity) IsEmpty() bool {
return len(te.Text) == 0 return len(te.Text) == 0
} }
func (te *TextEntity) AdjustStyle(fn AdjustStyleFunc) Entity { func (te *TextEntity) AdjustStyle(fn AdjustStyleFunc, reason AdjustStyleReason) Entity {
te.BaseEntity = te.BaseEntity.AdjustStyle(fn).(*BaseEntity) te.BaseEntity = te.BaseEntity.AdjustStyle(fn, reason).(*BaseEntity)
return te return te
} }
@@ -69,7 +69,7 @@ func (te *TextEntity) String() string {
return fmt.Sprintf("&html.TextEntity{Text=%s, Base=%s},\n", te.Text, te.BaseEntity) return fmt.Sprintf("&html.TextEntity{Text=%s, Base=%s},\n", te.Text, te.BaseEntity)
} }
func (te *TextEntity) Draw(screen mauview.Screen) { func (te *TextEntity) Draw(screen mauview.Screen, ctx DrawContext) {
width, _ := screen.Size() width, _ := screen.Size()
x := te.startX x := te.startX
for y, line := range te.buffer { for y, line := range te.buffer {
@@ -78,8 +78,8 @@ func (te *TextEntity) Draw(screen mauview.Screen) {
} }
} }
func (te *TextEntity) CalculateBuffer(width, startX int, bare bool) int { func (te *TextEntity) CalculateBuffer(width, startX int, ctx DrawContext) int {
te.BaseEntity.CalculateBuffer(width, startX, bare) te.BaseEntity.CalculateBuffer(width, startX, ctx)
if len(te.Text) == 0 { if len(te.Text) == 0 {
return te.startX return te.startX
} }
@@ -94,7 +94,7 @@ func (te *TextEntity) CalculateBuffer(width, startX int, bare bool) int {
for { for {
// TODO add option no wrap and character wrap options // TODO add option no wrap and character wrap options
extract := runewidth.Truncate(text, width-textStartX, "") extract := runewidth.Truncate(text, width-textStartX, "")
extract, wordWrapped := trim(extract, text, bare) extract, wordWrapped := trim(extract, text, ctx.BareMessages)
if !wordWrapped && textStartX > 0 { if !wordWrapped && textStartX > 0 {
if bufPtr < len(te.buffer) { if bufPtr < len(te.buffer) {
te.buffer[bufPtr] = "" te.buffer[bufPtr] = ""
+7 -18
View File
@@ -28,9 +28,7 @@ import (
type HTMLMessage struct { type HTMLMessage struct {
Root html.Entity Root html.Entity
FocusedBg tcell.Color
TextColor tcell.Color TextColor tcell.Color
focused bool
} }
func NewHTMLMessage(evt *muksevt.Event, displayname string, root html.Entity) *UIMessage { func NewHTMLMessage(evt *muksevt.Event, displayname string, root html.Entity) *UIMessage {
@@ -42,14 +40,10 @@ func NewHTMLMessage(evt *muksevt.Event, displayname string, root html.Entity) *U
func (hw *HTMLMessage) Clone() MessageRenderer { func (hw *HTMLMessage) Clone() MessageRenderer {
return &HTMLMessage{ return &HTMLMessage{
Root: hw.Root.Clone(), Root: hw.Root.Clone(),
FocusedBg: hw.FocusedBg,
} }
} }
func (hw *HTMLMessage) Draw(screen mauview.Screen) { func (hw *HTMLMessage) Draw(screen mauview.Screen, msg *UIMessage) {
if hw.focused {
screen.SetStyle(tcell.StyleDefault.Background(hw.FocusedBg).Foreground(hw.TextColor))
}
if hw.TextColor != tcell.ColorDefault { if hw.TextColor != tcell.ColorDefault {
hw.Root.AdjustStyle(func(style tcell.Style) tcell.Style { hw.Root.AdjustStyle(func(style tcell.Style) tcell.Style {
fg, _, _ := style.Decompose() fg, _, _ := style.Decompose()
@@ -57,18 +51,10 @@ func (hw *HTMLMessage) Draw(screen mauview.Screen) {
return style.Foreground(hw.TextColor) return style.Foreground(hw.TextColor)
} }
return style return style
}) }, html.AdjustStyleReasonNormal)
} }
screen.Clear() screen.Clear()
hw.Root.Draw(screen) hw.Root.Draw(screen, html.DrawContext{IsSelected: msg.IsSelected})
}
func (hw *HTMLMessage) Focus() {
hw.focused = true
}
func (hw *HTMLMessage) Blur() {
hw.focused = false
} }
func (hw *HTMLMessage) OnKeyEvent(event mauview.KeyEvent) bool { func (hw *HTMLMessage) OnKeyEvent(event mauview.KeyEvent) bool {
@@ -90,7 +76,10 @@ func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width
// TODO account for bare messages in initial startX // TODO account for bare messages in initial startX
startX := 0 startX := 0
hw.TextColor = msg.TextColor() hw.TextColor = msg.TextColor()
hw.Root.CalculateBuffer(width, startX, preferences.BareMessageView) hw.Root.CalculateBuffer(width, startX, html.DrawContext{
IsSelected: msg.IsSelected,
BareMessages: preferences.BareMessageView,
})
} }
func (hw *HTMLMessage) Height() int { func (hw *HTMLMessage) Height() int {
+1 -1
View File
@@ -59,7 +59,7 @@ const RedactionMaxWidth = 40
var RedactionStyle = tcell.StyleDefault.Foreground(tcell.NewRGBColor(50, 0, 0)) var RedactionStyle = tcell.StyleDefault.Foreground(tcell.NewRGBColor(50, 0, 0))
func (msg *RedactedMessage) Draw(screen mauview.Screen) { func (msg *RedactedMessage) Draw(screen mauview.Screen, _ *UIMessage) {
w, _ := screen.Size() w, _ := screen.Size()
for x := 0; x < w && x < RedactionMaxWidth; x++ { for x := 0; x < w && x < RedactionMaxWidth; x++ {
screen.SetContent(x, 0, RedactionChar, nil, RedactionStyle) screen.SetContent(x, 0, RedactionChar, nil, RedactionStyle)
+2 -2
View File
@@ -21,9 +21,9 @@ import (
"time" "time"
"go.mau.fi/mauview" "go.mau.fi/mauview"
"maunium.net/go/gomuks/matrix/muksevt"
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/matrix/muksevt"
"maunium.net/go/gomuks/ui/messages/tstring" "maunium.net/go/gomuks/ui/messages/tstring"
) )
@@ -95,7 +95,7 @@ func (msg *TextMessage) Height() int {
return len(msg.buffer) return len(msg.buffer)
} }
func (msg *TextMessage) Draw(screen mauview.Screen) { func (msg *TextMessage) Draw(screen mauview.Screen, _ *UIMessage) {
for y, line := range msg.buffer { for y, line := range msg.buffer {
line.Draw(screen, 0, y) line.Draw(screen, 0, y)
} }