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

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.

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()

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)
} }

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)
} }

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")
} }

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)
} }

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
} }

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
} }

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 {

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

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)

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()
} }

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
ui/messages/html/spoiler.go Normal file
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()
}

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] = ""

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 {
@ -41,15 +39,11 @@ 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 {

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)

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)
} }