Convert message buffer to use custom colorable strings
This commit is contained in:
parent
b6e58e83a8
commit
ee67c1446c
@ -87,8 +87,9 @@ type MessageMeta interface {
|
|||||||
SenderColor() tcell.Color
|
SenderColor() tcell.Color
|
||||||
TextColor() tcell.Color
|
TextColor() tcell.Color
|
||||||
TimestampColor() tcell.Color
|
TimestampColor() tcell.Color
|
||||||
Timestamp() string
|
Timestamp() time.Time
|
||||||
Date() string
|
FormatTime() string
|
||||||
|
FormatDate() string
|
||||||
CopyFrom(from MessageMeta)
|
CopyFrom(from MessageMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ type MessageView struct {
|
|||||||
messageIDs map[string]messages.UIMessage
|
messageIDs map[string]messages.UIMessage
|
||||||
messages []messages.UIMessage
|
messages []messages.UIMessage
|
||||||
|
|
||||||
textBuffer []string
|
textBuffer []messages.UIString
|
||||||
metaBuffer []ifc.MessageMeta
|
metaBuffer []ifc.MessageMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,14 +57,12 @@ func NewMessageView() *MessageView {
|
|||||||
return &MessageView{
|
return &MessageView{
|
||||||
Box: tview.NewBox(),
|
Box: tview.NewBox(),
|
||||||
MaxSenderWidth: 15,
|
MaxSenderWidth: 15,
|
||||||
DateFormat: "January _2, 2006",
|
TimestampWidth: len(messages.TimeFormat),
|
||||||
TimestampFormat: "15:04:05",
|
|
||||||
TimestampWidth: 8,
|
|
||||||
ScrollOffset: 0,
|
ScrollOffset: 0,
|
||||||
|
|
||||||
messages: make([]messages.UIMessage, 0),
|
messages: make([]messages.UIMessage, 0),
|
||||||
messageIDs: make(map[string]messages.UIMessage),
|
messageIDs: make(map[string]messages.UIMessage),
|
||||||
textBuffer: make([]string, 0),
|
textBuffer: make([]messages.UIString, 0),
|
||||||
metaBuffer: make([]ifc.MessageMeta, 0),
|
metaBuffer: make([]ifc.MessageMeta, 0),
|
||||||
|
|
||||||
widestSender: 5,
|
widestSender: 5,
|
||||||
@ -75,10 +73,7 @@ func NewMessageView() *MessageView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage {
|
func (view *MessageView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage {
|
||||||
return messages.NewMessage(id, sender, msgtype, text,
|
return messages.NewMessage(id, sender, msgtype, text, timestamp, widget.GetHashColor(sender))
|
||||||
timestamp.Format(view.TimestampFormat),
|
|
||||||
timestamp.Format(view.DateFormat),
|
|
||||||
widget.GetHashColor(sender))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) SaveHistory(path string) error {
|
func (view *MessageView) SaveHistory(path string) error {
|
||||||
@ -152,10 +147,10 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, messageExists := view.messageIDs[message.ID()]
|
oldMsg, messageExists := view.messageIDs[message.ID()]
|
||||||
if msg != nil && messageExists {
|
if messageExists {
|
||||||
msg.CopyFrom(message)
|
oldMsg.CopyFrom(message)
|
||||||
message = msg
|
message = oldMsg
|
||||||
direction = ifc.IgnoreMessage
|
direction = ifc.IgnoreMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +168,8 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
|
|||||||
view.appendBuffer(message)
|
view.appendBuffer(message)
|
||||||
} else if direction == ifc.PrependMessage {
|
} else if direction == ifc.PrependMessage {
|
||||||
view.messages = append([]messages.UIMessage{message}, view.messages...)
|
view.messages = append([]messages.UIMessage{message}, view.messages...)
|
||||||
|
} else {
|
||||||
|
view.replaceBuffer(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
view.messageIDs[message.ID()] = message
|
view.messageIDs[message.ID()] = message
|
||||||
@ -181,9 +178,12 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
|
|||||||
func (view *MessageView) appendBuffer(message messages.UIMessage) {
|
func (view *MessageView) appendBuffer(message messages.UIMessage) {
|
||||||
if len(view.metaBuffer) > 0 {
|
if len(view.metaBuffer) > 0 {
|
||||||
prevMeta := view.metaBuffer[len(view.metaBuffer)-1]
|
prevMeta := view.metaBuffer[len(view.metaBuffer)-1]
|
||||||
if prevMeta != nil && prevMeta.Date() != message.Date() {
|
if prevMeta != nil && prevMeta.FormatDate() != message.FormatDate() {
|
||||||
view.textBuffer = append(view.textBuffer, fmt.Sprintf("Date changed to %s", message.Date()))
|
view.textBuffer = append(view.textBuffer, messages.NewColorUIString(
|
||||||
view.metaBuffer = append(view.metaBuffer, &messages.BasicMeta{BTextColor: tcell.ColorGreen})
|
fmt.Sprintf("Date changed to %s", message.FormatDate()),
|
||||||
|
tcell.ColorGreen))
|
||||||
|
view.metaBuffer = append(view.metaBuffer, &messages.BasicMeta{
|
||||||
|
BTimestampColor: tcell.ColorDefault, BTextColor: tcell.ColorGreen})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,13 +194,42 @@ func (view *MessageView) appendBuffer(message messages.UIMessage) {
|
|||||||
view.prevMsgCount++
|
view.prevMsgCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *MessageView) replaceBuffer(message messages.UIMessage) {
|
||||||
|
start := -1
|
||||||
|
end := -1
|
||||||
|
for index, meta := range view.metaBuffer {
|
||||||
|
if meta == message {
|
||||||
|
if start == -1 {
|
||||||
|
start = index
|
||||||
|
}
|
||||||
|
end = index
|
||||||
|
} else if start != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(view.textBuffer) > end {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
view.textBuffer = append(append(view.textBuffer[0:start], message.Buffer()...), view.textBuffer[end:]...)
|
||||||
|
if len(message.Buffer()) != end - start + 1 {
|
||||||
|
debug.Print(end, "-", start, "!=", len(message.Buffer()))
|
||||||
|
metaBuffer := view.metaBuffer[0:start]
|
||||||
|
for range message.Buffer() {
|
||||||
|
metaBuffer = append(metaBuffer, message)
|
||||||
|
}
|
||||||
|
view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (view *MessageView) recalculateBuffers() {
|
func (view *MessageView) recalculateBuffers() {
|
||||||
_, _, width, height := view.GetInnerRect()
|
_, _, width, height := view.GetInnerRect()
|
||||||
|
|
||||||
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
||||||
recalculateMessageBuffers := width != view.prevWidth
|
recalculateMessageBuffers := width != view.prevWidth
|
||||||
if height != view.prevHeight || recalculateMessageBuffers || len(view.messages) != view.prevMsgCount {
|
if height != view.prevHeight || recalculateMessageBuffers || len(view.messages) != view.prevMsgCount {
|
||||||
view.textBuffer = []string{}
|
view.textBuffer = []messages.UIString{}
|
||||||
view.metaBuffer = []ifc.MessageMeta{}
|
view.metaBuffer = []ifc.MessageMeta{}
|
||||||
view.prevMsgCount = 0
|
view.prevMsgCount = 0
|
||||||
for _, message := range view.messages {
|
for _, message := range view.messages {
|
||||||
@ -219,7 +248,7 @@ const PaddingAtTop = 5
|
|||||||
func (view *MessageView) AddScrollOffset(diff int) {
|
func (view *MessageView) AddScrollOffset(diff int) {
|
||||||
_, _, _, height := view.GetInnerRect()
|
_, _, _, height := view.GetInnerRect()
|
||||||
|
|
||||||
totalHeight := len(view.textBuffer)
|
totalHeight := view.TotalHeight()
|
||||||
if diff >= 0 && view.ScrollOffset+diff >= totalHeight-height+PaddingAtTop {
|
if diff >= 0 && view.ScrollOffset+diff >= totalHeight-height+PaddingAtTop {
|
||||||
view.ScrollOffset = totalHeight - height + PaddingAtTop
|
view.ScrollOffset = totalHeight - height + PaddingAtTop
|
||||||
} else {
|
} else {
|
||||||
@ -285,7 +314,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
x, y, _, height := view.GetInnerRect()
|
x, y, _, height := view.GetInnerRect()
|
||||||
view.recalculateBuffers()
|
view.recalculateBuffers()
|
||||||
|
|
||||||
if len(view.textBuffer) == 0 {
|
if view.TotalHeight() == 0 {
|
||||||
widget.WriteLineSimple(screen, "It's quite empty in here.", x, y+height)
|
widget.WriteLineSimple(screen, "It's quite empty in here.", x, y+height)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -294,7 +323,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
messageX := usernameX + view.widestSender + SenderMessageGap
|
messageX := usernameX + view.widestSender + SenderMessageGap
|
||||||
separatorX := usernameX + view.widestSender + SenderSeparatorGap
|
separatorX := usernameX + view.widestSender + SenderSeparatorGap
|
||||||
|
|
||||||
indexOffset := len(view.textBuffer) - view.ScrollOffset - height
|
indexOffset := view.TotalHeight() - view.ScrollOffset - height
|
||||||
if indexOffset <= -PaddingAtTop {
|
if indexOffset <= -PaddingAtTop {
|
||||||
message := "Scroll up to load more messages."
|
message := "Scroll up to load more messages."
|
||||||
if view.LoadingMessages {
|
if view.LoadingMessages {
|
||||||
@ -312,7 +341,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
// Black magic (aka math) used to figure out where the scroll bar should be put.
|
// Black magic (aka math) used to figure out where the scroll bar should be put.
|
||||||
{
|
{
|
||||||
viewportHeight := float64(height)
|
viewportHeight := float64(height)
|
||||||
contentHeight := float64(len(view.textBuffer))
|
contentHeight := float64(view.TotalHeight())
|
||||||
|
|
||||||
scrollBarHeight = int(math.Ceil(viewportHeight / (contentHeight / viewportHeight)))
|
scrollBarHeight = int(math.Ceil(viewportHeight / (contentHeight / viewportHeight)))
|
||||||
|
|
||||||
@ -328,12 +357,12 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
if index < 0 {
|
if index < 0 {
|
||||||
skippedLines++
|
skippedLines++
|
||||||
continue
|
continue
|
||||||
} else if index >= len(view.textBuffer) {
|
} else if index >= view.TotalHeight() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
showScrollbar := line-skippedLines >= scrollBarPos-scrollBarHeight && line-skippedLines < scrollBarPos
|
showScrollbar := line-skippedLines >= scrollBarPos-scrollBarHeight && line-skippedLines < scrollBarPos
|
||||||
isTop := firstLine && view.ScrollOffset+height >= len(view.textBuffer)
|
isTop := firstLine && view.ScrollOffset+height >= view.TotalHeight()
|
||||||
isBottom := line == height-1 && view.ScrollOffset == 0
|
isBottom := line == height-1 && view.ScrollOffset == 0
|
||||||
|
|
||||||
borderChar, borderStyle := getScrollbarStyle(showScrollbar, isTop, isBottom)
|
borderChar, borderStyle := getScrollbarStyle(showScrollbar, isTop, isBottom)
|
||||||
@ -344,8 +373,8 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
|
|
||||||
text, meta := view.textBuffer[index], view.metaBuffer[index]
|
text, meta := view.textBuffer[index], view.metaBuffer[index]
|
||||||
if meta != prevMeta {
|
if meta != prevMeta {
|
||||||
if len(meta.Timestamp()) > 0 {
|
if len(meta.FormatTime()) > 0 {
|
||||||
widget.WriteLineSimpleColor(screen, meta.Timestamp(), x, y+line, meta.TimestampColor())
|
widget.WriteLineSimpleColor(screen, meta.FormatTime(), x, y+line, meta.TimestampColor())
|
||||||
}
|
}
|
||||||
if prevMeta == nil || meta.Sender() != prevMeta.Sender() {
|
if prevMeta == nil || meta.Sender() != prevMeta.Sender() {
|
||||||
widget.WriteLineColor(
|
widget.WriteLineColor(
|
||||||
@ -355,6 +384,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
}
|
}
|
||||||
prevMeta = meta
|
prevMeta = meta
|
||||||
}
|
}
|
||||||
widget.WriteLineSimpleColor(screen, text, messageX, y+line, meta.TextColor())
|
|
||||||
|
text.Draw(screen, messageX, y+line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,13 @@
|
|||||||
|
|
||||||
package messages
|
package messages
|
||||||
|
|
||||||
import "maunium.net/go/gomuks/interface"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
)
|
||||||
|
|
||||||
// Message is a wrapper for the content and metadata of a Matrix message intended to be displayed.
|
// Message is a wrapper for the content and metadata of a Matrix message intended to be displayed.
|
||||||
type UIMessage interface {
|
type UIMessage interface {
|
||||||
@ -24,6 +30,154 @@ type UIMessage interface {
|
|||||||
|
|
||||||
CalculateBuffer(width int)
|
CalculateBuffer(width int)
|
||||||
RecalculateBuffer()
|
RecalculateBuffer()
|
||||||
Buffer() []string
|
Buffer() []UIString
|
||||||
Height() int
|
Height() int
|
||||||
|
|
||||||
|
RealSender() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cell struct {
|
||||||
|
Char rune
|
||||||
|
Style tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStyleCell(char rune, style tcell.Style) Cell {
|
||||||
|
return Cell{char, style}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorCell(char rune, color tcell.Color) Cell {
|
||||||
|
return Cell{char, tcell.StyleDefault.Foreground(color)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCell(char rune) Cell {
|
||||||
|
return Cell{char, tcell.StyleDefault}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell Cell) RuneWidth() int {
|
||||||
|
return runewidth.RuneWidth(cell.Char)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cell Cell) Draw(screen tcell.Screen, x, y int) (chWidth int) {
|
||||||
|
chWidth = cell.RuneWidth()
|
||||||
|
for runeWidthOffset := 0; runeWidthOffset < chWidth; runeWidthOffset++ {
|
||||||
|
screen.SetContent(x+runeWidthOffset, y, cell.Char, nil, cell.Style)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type UIString []Cell
|
||||||
|
|
||||||
|
func NewUIString(str string) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewCell(char)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorUIString(str string, color tcell.Color) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewColorCell(char, color)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStyleUIString(str string, style tcell.Style) UIString {
|
||||||
|
newStr := make([]Cell, len(str))
|
||||||
|
for i, char := range str {
|
||||||
|
newStr[i] = NewStyleCell(char, style)
|
||||||
|
}
|
||||||
|
return newStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Colorize(from, to int, color tcell.Color) {
|
||||||
|
for i := from; i < to; i++ {
|
||||||
|
str[i].Style = str[i].Style.Foreground(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Draw(screen tcell.Screen, x, y int) {
|
||||||
|
offsetX := 0
|
||||||
|
for _, cell := range str {
|
||||||
|
offsetX += cell.Draw(screen, x+offsetX, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) RuneWidth() (width int) {
|
||||||
|
for _, cell := range str {
|
||||||
|
width += runewidth.RuneWidth(cell.Char)
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) String() string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for _, cell := range str {
|
||||||
|
buf.WriteRune(cell.Char)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate return string truncated with w cells
|
||||||
|
func (str UIString) Truncate(w int) UIString {
|
||||||
|
if str.RuneWidth() <= w {
|
||||||
|
return str[:]
|
||||||
|
}
|
||||||
|
width := 0
|
||||||
|
i := 0
|
||||||
|
for ; i < len(str); i++ {
|
||||||
|
cw := runewidth.RuneWidth(str[i].Char)
|
||||||
|
if width+cw > w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
width += cw
|
||||||
|
}
|
||||||
|
return str[0:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) IndexFrom(r rune, from int) int {
|
||||||
|
for i := from; i < len(str); i++ {
|
||||||
|
if str[i].Char == r {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Index(r rune) int {
|
||||||
|
return str.IndexFrom(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Count(r rune) (counter int) {
|
||||||
|
index := 0
|
||||||
|
for {
|
||||||
|
index = str.IndexFrom(r, index)
|
||||||
|
if index < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (str UIString) Split(sep rune) []UIString {
|
||||||
|
a := make([]UIString, str.Count(sep)+1)
|
||||||
|
i := 0
|
||||||
|
orig := str
|
||||||
|
for {
|
||||||
|
m := orig.Index(sep)
|
||||||
|
if m < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
a[i] = orig[:m]
|
||||||
|
orig = orig[m+1:]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
a[i] = orig
|
||||||
|
return a[:i+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateFormat = "January _2, 2006"
|
||||||
|
const TimeFormat = "15:04:05"
|
||||||
|
@ -17,13 +17,16 @@
|
|||||||
package messages
|
package messages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicMeta is a simple variable store implementation of MessageMeta.
|
// BasicMeta is a simple variable store implementation of MessageMeta.
|
||||||
type BasicMeta struct {
|
type BasicMeta struct {
|
||||||
BSender, BTimestamp, BDate string
|
BSender string
|
||||||
|
BTimestamp time.Time
|
||||||
BSenderColor, BTextColor, BTimestampColor tcell.Color
|
BSenderColor, BTextColor, BTimestampColor tcell.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,14 +40,19 @@ func (meta *BasicMeta) SenderColor() tcell.Color {
|
|||||||
return meta.BSenderColor
|
return meta.BSenderColor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp returns the formatted time when the message was sent.
|
// Timestamp returns the full time when the message was sent.
|
||||||
func (meta *BasicMeta) Timestamp() string {
|
func (meta *BasicMeta) Timestamp() time.Time {
|
||||||
return meta.BTimestamp
|
return meta.BTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date returns the formatted date when the message was sent.
|
// FormatTime returns the formatted time when the message was sent.
|
||||||
func (meta *BasicMeta) Date() string {
|
func (meta *BasicMeta) FormatTime() string {
|
||||||
return meta.BDate
|
return meta.BTimestamp.Format(TimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDate returns the formatted date when the message was sent.
|
||||||
|
func (meta *BasicMeta) FormatDate() string {
|
||||||
|
return meta.BTimestamp.Format(DateFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextColor returns the color the actual content of the message should be shown in.
|
// TextColor returns the color the actual content of the message should be shown in.
|
||||||
@ -63,7 +71,6 @@ func (meta *BasicMeta) TimestampColor() tcell.Color {
|
|||||||
func (meta *BasicMeta) CopyFrom(from ifc.MessageMeta) {
|
func (meta *BasicMeta) CopyFrom(from ifc.MessageMeta) {
|
||||||
meta.BSender = from.Sender()
|
meta.BSender = from.Sender()
|
||||||
meta.BTimestamp = from.Timestamp()
|
meta.BTimestamp = from.Timestamp()
|
||||||
meta.BDate = from.Date()
|
|
||||||
meta.BSenderColor = from.SenderColor()
|
meta.BSenderColor = from.SenderColor()
|
||||||
meta.BTextColor = from.TextColor()
|
meta.BTextColor = from.TextColor()
|
||||||
meta.BTimestampColor = from.TimestampColor()
|
meta.BTimestampColor = from.TimestampColor()
|
||||||
|
@ -20,10 +20,9 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,22 +35,20 @@ type UITextMessage struct {
|
|||||||
MsgType string
|
MsgType string
|
||||||
MsgSender string
|
MsgSender string
|
||||||
MsgSenderColor tcell.Color
|
MsgSenderColor tcell.Color
|
||||||
MsgTimestamp string
|
MsgTimestamp time.Time
|
||||||
MsgDate string
|
|
||||||
MsgText string
|
MsgText string
|
||||||
MsgState ifc.MessageState
|
MsgState ifc.MessageState
|
||||||
MsgIsHighlight bool
|
MsgIsHighlight bool
|
||||||
MsgIsService bool
|
MsgIsService bool
|
||||||
buffer []string
|
buffer []UIString
|
||||||
prevBufferWidth int
|
prevBufferWidth int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage creates a new Message object with the provided values and the default state.
|
// NewMessage creates a new Message object with the provided values and the default state.
|
||||||
func NewMessage(id, sender, msgtype, text, timestamp, date string, senderColor tcell.Color) UIMessage {
|
func NewMessage(id, sender, msgtype, text string, timestamp time.Time, senderColor tcell.Color) UIMessage {
|
||||||
return &UITextMessage{
|
return &UITextMessage{
|
||||||
MsgSender: sender,
|
MsgSender: sender,
|
||||||
MsgTimestamp: timestamp,
|
MsgTimestamp: timestamp,
|
||||||
MsgDate: date,
|
|
||||||
MsgSenderColor: senderColor,
|
MsgSenderColor: senderColor,
|
||||||
MsgType: msgtype,
|
MsgType: msgtype,
|
||||||
MsgText: text,
|
MsgText: text,
|
||||||
@ -66,18 +63,19 @@ func NewMessage(id, sender, msgtype, text, timestamp, date string, senderColor t
|
|||||||
// CopyFrom replaces the content of this message object with the content of the given object.
|
// CopyFrom replaces the content of this message object with the content of the given object.
|
||||||
func (msg *UITextMessage) CopyFrom(from ifc.MessageMeta) {
|
func (msg *UITextMessage) CopyFrom(from ifc.MessageMeta) {
|
||||||
msg.MsgSender = from.Sender()
|
msg.MsgSender = from.Sender()
|
||||||
msg.MsgTimestamp = from.Timestamp()
|
|
||||||
msg.MsgDate = from.Date()
|
|
||||||
msg.MsgSenderColor = from.SenderColor()
|
msg.MsgSenderColor = from.SenderColor()
|
||||||
|
|
||||||
fromMsg, ok := from.(UIMessage)
|
fromMsg, ok := from.(UIMessage)
|
||||||
if ok {
|
if ok {
|
||||||
|
msg.MsgSender = fromMsg.RealSender()
|
||||||
msg.MsgID = fromMsg.ID()
|
msg.MsgID = fromMsg.ID()
|
||||||
msg.MsgType = fromMsg.Type()
|
msg.MsgType = fromMsg.Type()
|
||||||
|
msg.MsgTimestamp = fromMsg.Timestamp()
|
||||||
msg.MsgText = fromMsg.Text()
|
msg.MsgText = fromMsg.Text()
|
||||||
msg.MsgState = fromMsg.State()
|
msg.MsgState = fromMsg.State()
|
||||||
msg.MsgIsService = fromMsg.IsService()
|
msg.MsgIsService = fromMsg.IsService()
|
||||||
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
msg.MsgIsHighlight = fromMsg.IsHighlight()
|
||||||
|
msg.buffer = nil
|
||||||
|
|
||||||
msg.RecalculateBuffer()
|
msg.RecalculateBuffer()
|
||||||
}
|
}
|
||||||
@ -105,6 +103,10 @@ func (msg *UITextMessage) Sender() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *UITextMessage) RealSender() string {
|
||||||
|
return msg.MsgSender
|
||||||
|
}
|
||||||
|
|
||||||
func (msg *UITextMessage) getStateSpecificColor() tcell.Color {
|
func (msg *UITextMessage) getStateSpecificColor() tcell.Color {
|
||||||
switch msg.MsgState {
|
switch msg.MsgState {
|
||||||
case ifc.MessageStateSending:
|
case ifc.MessageStateSending:
|
||||||
@ -176,7 +178,7 @@ func (msg *UITextMessage) RecalculateBuffer() {
|
|||||||
//
|
//
|
||||||
// N.B. This will NOT automatically calculate the buffer if it hasn't been
|
// N.B. This will NOT automatically calculate the buffer if it hasn't been
|
||||||
// calculated already, as that requires the target width.
|
// calculated already, as that requires the target width.
|
||||||
func (msg *UITextMessage) Buffer() []string {
|
func (msg *UITextMessage) Buffer() []UIString {
|
||||||
return msg.buffer
|
return msg.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,14 +187,19 @@ func (msg *UITextMessage) Height() int {
|
|||||||
return len(msg.buffer)
|
return len(msg.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp returns the formatted time when the message was sent.
|
// Timestamp returns the full timestamp when the message was sent.
|
||||||
func (msg *UITextMessage) Timestamp() string {
|
func (msg *UITextMessage) Timestamp() time.Time {
|
||||||
return msg.MsgTimestamp
|
return msg.MsgTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date returns the formatted date when the message was sent.
|
// FormatTime returns the formatted time when the message was sent.
|
||||||
func (msg *UITextMessage) Date() string {
|
func (msg *UITextMessage) FormatTime() string {
|
||||||
return msg.MsgDate
|
return msg.MsgTimestamp.Format(TimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDate returns the formatted date when the message was sent.
|
||||||
|
func (msg *UITextMessage) FormatDate() string {
|
||||||
|
return msg.MsgTimestamp.Format(DateFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *UITextMessage) ID() string {
|
func (msg *UITextMessage) ID() string {
|
||||||
@ -259,30 +266,31 @@ func (msg *UITextMessage) CalculateBuffer(width int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.buffer = []string{}
|
msg.buffer = []UIString{}
|
||||||
text := msg.MsgText
|
text := NewColorUIString(msg.Text(), msg.TextColor())
|
||||||
if msg.MsgType == "m.emote" {
|
if msg.MsgType == "m.emote" {
|
||||||
text = fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText)
|
text = NewColorUIString(fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText), msg.TextColor())
|
||||||
|
text.Colorize(2, 2+len(msg.MsgSender), msg.SenderColor())
|
||||||
}
|
}
|
||||||
|
|
||||||
forcedLinebreaks := strings.Split(text, "\n")
|
forcedLinebreaks := text.Split('\n')
|
||||||
newlines := 0
|
newlines := 0
|
||||||
for _, str := range forcedLinebreaks {
|
for _, str := range forcedLinebreaks {
|
||||||
if len(str) == 0 && newlines < 1 {
|
if len(str) == 0 && newlines < 1 {
|
||||||
msg.buffer = append(msg.buffer, "")
|
msg.buffer = append(msg.buffer, UIString{})
|
||||||
newlines++
|
newlines++
|
||||||
} else {
|
} else {
|
||||||
newlines = 0
|
newlines = 0
|
||||||
}
|
}
|
||||||
// From tview/textview.go#reindexBuffer()
|
// Mostly from tview/textview.go#reindexBuffer()
|
||||||
for len(str) > 0 {
|
for len(str) > 0 {
|
||||||
extract := runewidth.Truncate(str, width, "")
|
extract := str.Truncate(width)
|
||||||
if len(extract) < len(str) {
|
if len(extract) < len(str) {
|
||||||
if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
|
if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 {
|
||||||
extract = str[:len(extract)+spaces[1]]
|
extract = str[:len(extract)+spaces[1]]
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := boundaryPattern.FindAllStringIndex(extract, -1)
|
matches := boundaryPattern.FindAllStringIndex(extract.String(), -1)
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
extract = extract[:matches[len(matches)-1][1]]
|
extract = extract[:matches[len(matches)-1][1]]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user