aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface/ui.go3
-rw-r--r--lib/ansimage/ansimage.go10
-rw-r--r--ui/message-view.go4
-rw-r--r--ui/messages/base.go236
-rw-r--r--ui/messages/expandedtextmessage.go71
-rw-r--r--ui/messages/imagemessage.go66
-rw-r--r--ui/messages/parser.go4
-rw-r--r--ui/messages/textbase.go84
-rw-r--r--ui/messages/textmessage.go304
-rw-r--r--ui/view-main.go13
10 files changed, 434 insertions, 361 deletions
diff --git a/interface/ui.go b/interface/ui.go
index fed1a73..e6071fc 100644
--- a/interface/ui.go
+++ b/interface/ui.go
@@ -119,8 +119,7 @@ type Message interface {
SetType(msgtype string)
Type() string
- SetText(text string)
- Text() string
+ NotificationContent() string
SetState(state MessageState)
State() MessageState
diff --git a/lib/ansimage/ansimage.go b/lib/ansimage/ansimage.go
index a41c833..b117b3d 100644
--- a/lib/ansimage/ansimage.go
+++ b/lib/ansimage/ansimage.go
@@ -124,8 +124,8 @@ func (ai *ANSImage) Render() []tstring.TString {
rows := make([]tstring.TString, ai.h/2)
for y := 0; y < ai.h; y += ai.maxprocs {
ch := make(chan renderData, ai.maxprocs)
- for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
- go func(r, y int) {
+ for n, row := 0, y; (n <= ai.maxprocs) && (2*row+1 < ai.h); n, row = n+1, y+n {
+ go func(row, y int) {
str := make(tstring.TString, ai.w)
for x := 0; x < ai.w; x++ {
topPixel := ai.pixmap[y][x]
@@ -139,10 +139,10 @@ func (ai *ANSImage) Render() []tstring.TString {
Style: tcell.StyleDefault.Background(topColor).Foreground(bottomColor),
}
}
- ch <- renderData{row: r, render: str}
- }(r, 2*r)
+ ch <- renderData{row: row, render: str}
+ }(row, 2*row)
}
- for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
+ for n, row := 0, y; (n <= ai.maxprocs) && (2*row+1 < ai.h); n, row = n+1, y+n {
data := <-ch
rows[data.row] = data.render
}
diff --git a/ui/message-view.go b/ui/message-view.go
index 5584d3e..9216aed 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -265,11 +265,11 @@ func (view *MessageView) HandleClick(x, y int, button tcell.ButtonMask) {
}
message := view.metaBuffer[line]
- imageMessage, ok := message.(*messages.UIImageMessage)
+ imageMessage, ok := message.(*messages.ImageMessage)
if !ok {
uiMessage, ok := message.(messages.UIMessage)
if ok {
- debug.Print("Message clicked:", uiMessage.Text())
+ debug.Print("Message clicked:", uiMessage.NotificationContent())
}
return
}
diff --git a/ui/messages/base.go b/ui/messages/base.go
new file mode 100644
index 0000000..deb153f
--- /dev/null
+++ b/ui/messages/base.go
@@ -0,0 +1,236 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package messages
+
+import (
+ "encoding/gob"
+ "time"
+
+ "maunium.net/go/gomuks/interface"
+ "maunium.net/go/gomuks/ui/messages/tstring"
+ "maunium.net/go/gomuks/ui/widget"
+ "maunium.net/go/tcell"
+)
+
+func init() {
+ gob.Register(&BaseMessage{})
+}
+
+type BaseMessage struct {
+ MsgID string
+ MsgType string
+ MsgSender string
+ MsgSenderColor tcell.Color
+ MsgTimestamp time.Time
+ MsgState ifc.MessageState
+ MsgIsHighlight bool
+ MsgIsService bool
+ buffer []tstring.TString
+ prevBufferWidth int
+}
+
+func newBaseMessage(id, sender, msgtype string, timestamp time.Time) BaseMessage {
+ return BaseMessage{
+ MsgSender: sender,
+ MsgTimestamp: timestamp,
+ MsgSenderColor: widget.GetHashColor(sender),
+ MsgType: msgtype,
+ MsgID: id,
+ prevBufferWidth: 0,
+ MsgState: ifc.MessageStateDefault,
+ MsgIsHighlight: false,
+ MsgIsService: false,
+ }
+}
+
+func (msg *BaseMessage) RegisterGomuks(gmx ifc.Gomuks) {}
+
+// CopyFrom replaces the content of this message object with the content of the given object.
+func (msg *BaseMessage) CopyFrom(from ifc.MessageMeta) {
+ msg.MsgSender = from.Sender()
+ msg.MsgSenderColor = from.SenderColor()
+
+ fromMsg, ok := from.(UIMessage)
+ if ok {
+ msg.MsgSender = fromMsg.RealSender()
+ msg.MsgID = fromMsg.ID()
+ msg.MsgType = fromMsg.Type()
+ msg.MsgTimestamp = fromMsg.Timestamp()
+ msg.MsgState = fromMsg.State()
+ msg.MsgIsService = fromMsg.IsService()
+ msg.MsgIsHighlight = fromMsg.IsHighlight()
+ msg.buffer = nil
+ }
+}
+
+// Sender gets the string that should be displayed as the sender of this message.
+//
+// If the message is being sent, the sender is "Sending...".
+// If sending has failed, the sender is "Error".
+// If the message is an emote, the sender is blank.
+// In any other case, the sender is the display name of the user who sent the message.
+func (msg *BaseMessage) Sender() string {
+ switch msg.MsgState {
+ case ifc.MessageStateSending:
+ return "Sending..."
+ case ifc.MessageStateFailed:
+ return "Error"
+ }
+ switch msg.MsgType {
+ case "m.emote":
+ // Emotes don't show a separate sender, it's included in the buffer.
+ return ""
+ default:
+ return msg.MsgSender
+ }
+}
+
+func (msg *BaseMessage) RealSender() string {
+ return msg.MsgSender
+}
+
+func (msg *BaseMessage) getStateSpecificColor() tcell.Color {
+ switch msg.MsgState {
+ case ifc.MessageStateSending:
+ return tcell.ColorGray
+ case ifc.MessageStateFailed:
+ return tcell.ColorRed
+ case ifc.MessageStateDefault:
+ fallthrough
+ default:
+ return tcell.ColorDefault
+ }
+}
+
+// SenderColor returns the color the name of the sender should be shown in.
+//
+// If the message is being sent, the color is gray.
+// If sending has failed, the color is red.
+//
+// In any other case, the color is whatever is specified in the Message struct.
+// Usually that means it is the hash-based color of the sender (see ui/widget/color.go)
+func (msg *BaseMessage) SenderColor() tcell.Color {
+ stateColor := msg.getStateSpecificColor()
+ switch {
+ case stateColor != tcell.ColorDefault:
+ return stateColor
+ case msg.MsgIsService:
+ return tcell.ColorGray
+ default:
+ return msg.MsgSenderColor
+ }
+}
+
+// TextColor returns the color the actual content of the message should be shown in.
+func (msg *BaseMessage) TextColor() tcell.Color {
+ stateColor := msg.getStateSpecificColor()
+ switch {
+ case stateColor != tcell.ColorDefault:
+ return stateColor
+ case msg.MsgIsService:
+ fallthrough
+ case msg.MsgType == "m.notice":
+ return tcell.ColorGray
+ case msg.MsgIsHighlight:
+ return tcell.ColorYellow
+ case msg.MsgType == "m.room.member":
+ return tcell.ColorGreen
+ default:
+ return tcell.ColorDefault
+ }
+}
+
+// TimestampColor returns the color the timestamp should be shown in.
+//
+// As with SenderColor(), messages being sent and messages that failed to be sent are
+// gray and red respectively.
+//
+// However, other messages are the default color instead of a color stored in the struct.
+func (msg *BaseMessage) TimestampColor() tcell.Color {
+ return msg.getStateSpecificColor()
+}
+
+// Buffer returns the computed text buffer.
+//
+// The buffer contains the text of the message split into lines with a maximum
+// width of whatever was provided to CalculateBuffer().
+//
+// N.B. This will NOT automatically calculate the buffer if it hasn't been
+// calculated already, as that requires the target width.
+func (msg *BaseMessage) Buffer() []tstring.TString {
+ return msg.buffer
+}
+
+// Height returns the number of rows in the computed buffer (see Buffer()).
+func (msg *BaseMessage) Height() int {
+ return len(msg.buffer)
+}
+
+// Timestamp returns the full timestamp when the message was sent.
+func (msg *BaseMessage) Timestamp() time.Time {
+ return msg.MsgTimestamp
+}
+
+// FormatTime returns the formatted time when the message was sent.
+func (msg *BaseMessage) FormatTime() string {
+ return msg.MsgTimestamp.Format(TimeFormat)
+}
+
+// FormatDate returns the formatted date when the message was sent.
+func (msg *BaseMessage) FormatDate() string {
+ return msg.MsgTimestamp.Format(DateFormat)
+}
+
+func (msg *BaseMessage) ID() string {
+ return msg.MsgID
+}
+
+func (msg *BaseMessage) SetID(id string) {
+ msg.MsgID = id
+}
+
+func (msg *BaseMessage) Type() string {
+ return msg.MsgType
+}
+
+func (msg *BaseMessage) SetType(msgtype string) {
+ msg.MsgType = msgtype
+}
+
+func (msg *BaseMessage) State() ifc.MessageState {
+ return msg.MsgState
+}
+
+func (msg *BaseMessage) SetState(state ifc.MessageState) {
+ msg.MsgState = state
+}
+
+func (msg *BaseMessage) IsHighlight() bool {
+ return msg.MsgIsHighlight
+}
+
+func (msg *BaseMessage) SetIsHighlight(isHighlight bool) {
+ msg.MsgIsHighlight = isHighlight
+}
+
+func (msg *BaseMessage) IsService() bool {
+ return msg.MsgIsService
+}
+
+func (msg *BaseMessage) SetIsService(isService bool) {
+ msg.MsgIsService = isService
+}
diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go
index 1d6030a..3ee15ad 100644
--- a/ui/messages/expandedtextmessage.go
+++ b/ui/messages/expandedtextmessage.go
@@ -22,65 +22,50 @@ import (
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/ui/messages/tstring"
- "maunium.net/go/gomuks/ui/widget"
)
func init() {
- gob.Register(&UITextMessage{})
- gob.Register(&UIExpandedTextMessage{})
+ gob.Register(&ExpandedTextMessage{})
}
-type UIExpandedTextMessage struct {
- UITextMessage
- MsgTStringText tstring.TString
+type ExpandedTextMessage struct {
+ BaseTextMessage
+ MsgText tstring.TString
}
-// NewExpandedTextMessage creates a new UIExpandedTextMessage object with the provided values and the default state.
+// NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state.
func NewExpandedTextMessage(id, sender, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
- return &UIExpandedTextMessage{
- UITextMessage{
- MsgSender: sender,
- MsgTimestamp: timestamp,
- MsgSenderColor: widget.GetHashColor(sender),
- MsgType: msgtype,
- MsgText: text.String(),
- MsgID: id,
- prevBufferWidth: 0,
- MsgState: ifc.MessageStateDefault,
- MsgIsHighlight: false,
- MsgIsService: false,
- },
- text,
+ return &ExpandedTextMessage{
+ BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
+ MsgText: text,
}
}
-func (msg *UIExpandedTextMessage) GetTStringText() tstring.TString {
- return msg.MsgTStringText
+func (msg *ExpandedTextMessage) GenerateText() tstring.TString {
+ return msg.MsgText
}
// CopyFrom replaces the content of this message object with the content of the given object.
-func (msg *UIExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
- msg.MsgSender = from.Sender()
- msg.MsgSenderColor = from.SenderColor()
+func (msg *ExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
+ msg.BaseTextMessage.CopyFrom(from)
- fromMsg, ok := from.(UIMessage)
+ fromExpandedMsg, ok := from.(*ExpandedTextMessage)
if ok {
- msg.MsgSender = fromMsg.RealSender()
- msg.MsgID = fromMsg.ID()
- msg.MsgType = fromMsg.Type()
- msg.MsgTimestamp = fromMsg.Timestamp()
- msg.MsgState = fromMsg.State()
- msg.MsgIsService = fromMsg.IsService()
- msg.MsgIsHighlight = fromMsg.IsHighlight()
- msg.buffer = nil
+ msg.MsgText = fromExpandedMsg.MsgText
+ }
- fromExpandedMsg, ok := from.(*UIExpandedTextMessage)
- if ok {
- msg.MsgTStringText = fromExpandedMsg.MsgTStringText
- } else {
- msg.MsgTStringText = tstring.NewColorTString(fromMsg.Text(), from.TextColor())
- }
+ msg.RecalculateBuffer()
+}
- msg.RecalculateBuffer()
- }
+func (msg *ExpandedTextMessage) NotificationContent() string {
+ return msg.MsgText.String()
+}
+
+func (msg *ExpandedTextMessage) CalculateBuffer(width int) {
+ msg.BaseTextMessage.calculateBufferWithText(msg.MsgText, width)
+}
+
+// RecalculateBuffer calculates the buffer again with the previously provided width.
+func (msg *ExpandedTextMessage) RecalculateBuffer() {
+ msg.CalculateBuffer(msg.prevBufferWidth)
}
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index 5282d44..2fbf6ae 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -28,16 +28,15 @@ import (
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/lib/ansimage"
"maunium.net/go/gomuks/ui/messages/tstring"
- "maunium.net/go/gomuks/ui/widget"
"maunium.net/go/tcell"
)
func init() {
- gob.Register(&UIImageMessage{})
+ gob.Register(&ImageMessage{})
}
-type UIImageMessage struct {
- UITextMessage
+type ImageMessage struct {
+ BaseMessage
Homeserver string
FileID string
data []byte
@@ -45,20 +44,10 @@ type UIImageMessage struct {
gmx ifc.Gomuks
}
-// NewImageMessage creates a new UIImageMessage object with the provided values and the default state.
+// NewImageMessage creates a new ImageMessage object with the provided values and the default state.
func NewImageMessage(gmx ifc.Gomuks, id, sender, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
- return &UIImageMessage{
- UITextMessage{
- MsgSender: sender,
- MsgTimestamp: timestamp,
- MsgSenderColor: widget.GetHashColor(sender),
- MsgType: msgtype,
- MsgID: id,
- prevBufferWidth: 0,
- MsgState: ifc.MessageStateDefault,
- MsgIsHighlight: false,
- MsgIsService: false,
- },
+ return &ImageMessage{
+ newBaseMessage(id, sender, msgtype, timestamp),
homeserver,
fileID,
data,
@@ -66,7 +55,7 @@ func NewImageMessage(gmx ifc.Gomuks, id, sender, msgtype, homeserver, fileID str
}
}
-func (msg *UIImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
+func (msg *ImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
msg.gmx = gmx
debug.Print(len(msg.data), msg.data)
@@ -78,7 +67,11 @@ func (msg *UIImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
}
}
-func (msg *UIImageMessage) updateData() {
+func (msg *ImageMessage) NotificationContent() string {
+ return "Sent an image"
+}
+
+func (msg *ImageMessage) updateData() {
debug.Print("Loading image:", msg.Homeserver, msg.FileID)
data, _, _, err := msg.gmx.Matrix().Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
if err != nil {
@@ -88,39 +81,26 @@ func (msg *UIImageMessage) updateData() {
msg.data = data
}
-func (msg *UIImageMessage) Path() string {
+func (msg *ImageMessage) Path() string {
return msg.gmx.Matrix().GetCachePath(msg.Homeserver, msg.FileID)
}
// CopyFrom replaces the content of this message object with the content of the given object.
-func (msg *UIImageMessage) CopyFrom(from ifc.MessageMeta) {
- msg.MsgSender = from.Sender()
- msg.MsgSenderColor = from.SenderColor()
+func (msg *ImageMessage) CopyFrom(from ifc.MessageMeta) {
+ msg.BaseMessage.CopyFrom(from)
- fromMsg, ok := from.(UIMessage)
+ fromImgMsg, ok := from.(*ImageMessage)
if ok {
- msg.MsgSender = fromMsg.RealSender()
- msg.MsgID = fromMsg.ID()
- msg.MsgType = fromMsg.Type()
- msg.MsgTimestamp = fromMsg.Timestamp()
- msg.MsgState = fromMsg.State()
- msg.MsgIsService = fromMsg.IsService()
- msg.MsgIsHighlight = fromMsg.IsHighlight()
- msg.buffer = nil
-
- fromImgMsg, ok := from.(*UIImageMessage)
- if ok {
- msg.data = fromImgMsg.data
- }
-
- msg.RecalculateBuffer()
+ msg.data = fromImgMsg.data
}
+
+ msg.RecalculateBuffer()
}
// CalculateBuffer generates the internal buffer for this message that consists
// of the text of this message split into lines at most as wide as the width
// parameter.
-func (msg *UIImageMessage) CalculateBuffer(width int) {
+func (msg *ImageMessage) CalculateBuffer(width int) {
if width < 2 {
return
}
@@ -135,3 +115,9 @@ func (msg *UIImageMessage) CalculateBuffer(width int) {
msg.buffer = image.Render()
msg.prevBufferWidth = width
}
+
+// RecalculateBuffer calculates the buffer again with the previously provided width.
+func (msg *ImageMessage) RecalculateBuffer() {
+ msg.CalculateBuffer(msg.prevBufferWidth)
+}
+
diff --git a/ui/messages/parser.go b/ui/messages/parser.go
index af76bf3..263bced 100644
--- a/ui/messages/parser.go
+++ b/ui/messages/parser.go
@@ -55,7 +55,7 @@ func ParseMessage(gmx ifc.Gomuks, evt *gomatrix.Event) UIMessage {
msgtype, _ := evt.Content["msgtype"].(string)
ts := unixToTime(evt.Timestamp)
switch msgtype {
- case "m.text", "m.notice":
+ case "m.text", "m.notice", "m.emote":
text, _ := evt.Content["body"].(string)
return NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
case "m.image":
@@ -86,7 +86,7 @@ func getMembershipEventContent(evt *gomatrix.Event) (sender string, text tstring
switch membership {
case "invite":
sender = "---"
- text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorYellow)
+ text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorGreen)
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
text.Colorize(len(evt.Sender)+len(" invited "), len(displayname), widget.GetHashColor(displayname))
case "join":
diff --git a/ui/messages/textbase.go b/ui/messages/textbase.go
new file mode 100644
index 0000000..d7eb16c
--- /dev/null
+++ b/ui/messages/textbase.go
@@ -0,0 +1,84 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package messages
+
+import (
+ "encoding/gob"
+ "regexp"
+ "time"
+
+ "maunium.net/go/gomuks/ui/messages/tstring"
+)
+
+func init() {
+ gob.Register(BaseTextMessage{})
+}
+
+type BaseTextMessage struct {
+ BaseMessage
+}
+
+func newBaseTextMessage(id, sender, msgtype string, timestamp time.Time) BaseTextMessage {
+ return BaseTextMessage{newBaseMessage(id, sender, msgtype, timestamp)}
+}
+
+// Regular expressions used to split lines when calculating the buffer.
+//
+// From tview/textview.go
+var (
+ boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
+ spacePattern = regexp.MustCompile(`\s+`)
+)
+
+// CalculateBuffer generates the internal buffer for this message that consists
+// of the text of this message split into lines at most as wide as the width
+// parameter.
+func (msg *BaseTextMessage) calculateBufferWithText(text tstring.TString, width int) {
+ if width < 2 {
+ return
+ }
+
+ msg.buffer = []tstring.TString{}
+
+ forcedLinebreaks := text.Split('\n')
+ newlines := 0
+ for _, str := range forcedLinebreaks {
+ if len(str) == 0 && newlines < 1 {
+ msg.buffer = append(msg.buffer, tstring.TString{})
+ newlines++
+ } else {
+ newlines = 0
+ }
+ // Mostly from tview/textview.go#reindexBuffer()
+ for len(str) > 0 {
+ extract := str.Truncate(width)
+ if len(extract) < len(str) {
+ if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 {
+ extract = str[:len(extract)+spaces[1]]
+ }
+
+ matches := boundaryPattern.FindAllStringIndex(extract.String(), -1)
+ if len(matches) > 0 {
+ extract = extract[:matches[len(matches)-1][1]]
+ }
+ }
+ msg.buffer = append(msg.buffer, extract)
+ str = str[len(extract):]
+ }
+ }
+ msg.prevBufferWidth = width
+}
diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go
index f630ef0..4c99e5b 100644
--- a/ui/messages/textmessage.go
+++ b/ui/messages/textmessage.go
@@ -19,302 +19,84 @@ package messages
import (
"encoding/gob"
"fmt"
- "regexp"
"time"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/ui/messages/tstring"
- "maunium.net/go/gomuks/ui/widget"
- "maunium.net/go/tcell"
)
func init() {
- gob.Register(&UITextMessage{})
+ gob.Register(&TextMessage{})
}
-type UITextMessage struct {
- MsgID string
- MsgType string
- MsgSender string
- MsgSenderColor tcell.Color
- MsgTimestamp time.Time
- MsgText string
- MsgState ifc.MessageState
- MsgIsHighlight bool
- MsgIsService bool
- text tstring.TString
- buffer []tstring.TString
- prevBufferWidth int
+type TextMessage struct {
+ BaseTextMessage
+ cache tstring.TString
+ MsgText string
}
// NewTextMessage creates a new UITextMessage object with the provided values and the default state.
func NewTextMessage(id, sender, msgtype, text string, timestamp time.Time) UIMessage {
- return &UITextMessage{
- MsgSender: sender,
- MsgTimestamp: timestamp,
- MsgSenderColor: widget.GetHashColor(sender),
- MsgType: msgtype,
+ return &TextMessage{
+ BaseTextMessage: newBaseTextMessage(id, sender, msgtype, timestamp),
MsgText: text,
- MsgID: id,
- prevBufferWidth: 0,
- MsgState: ifc.MessageStateDefault,
- MsgIsHighlight: false,
- MsgIsService: false,
}
}
-func (msg *UITextMessage) RegisterGomuks(gmx ifc.Gomuks) {}
-
-// CopyFrom replaces the content of this message object with the content of the given object.
-func (msg *UITextMessage) CopyFrom(from ifc.MessageMeta) {
- msg.MsgSender = from.Sender()
- msg.MsgSenderColor = from.SenderColor()
-
- fromMsg, ok := from.(UIMessage)
- if ok {
- msg.MsgSender = fromMsg.RealSender()
- msg.MsgID = fromMsg.ID()
- msg.MsgType = fromMsg.Type()
- msg.MsgTimestamp = fromMsg.Timestamp()
- msg.MsgText = fromMsg.Text()
- msg.MsgState = fromMsg.State()
- msg.MsgIsService = fromMsg.IsService()
- msg.MsgIsHighlight = fromMsg.IsHighlight()
- msg.buffer = nil
-
- msg.RecalculateBuffer()
- }
-}
-
-// Sender gets the string that should be displayed as the sender of this message.
-//
-// If the message is being sent, the sender is "Sending...".
-// If sending has failed, the sender is "Error".
-// If the message is an emote, the sender is blank.
-// In any other case, the sender is the display name of the user who sent the message.
-func (msg *UITextMessage) Sender() string {
- switch msg.MsgState {
- case ifc.MessageStateSending:
- return "Sending..."
- case ifc.MessageStateFailed:
- return "Error"
- }
- switch msg.MsgType {
- case "m.emote":
- // Emotes don't show a separate sender, it's included in the buffer.
- return ""
- default:
- return msg.MsgSender
- }
-}
-
-func (msg *UITextMessage) RealSender() string {
- return msg.MsgSender
-}
-
-func (msg *UITextMessage) getStateSpecificColor() tcell.Color {
- switch msg.MsgState {
- case ifc.MessageStateSending:
- return tcell.ColorGray
- case ifc.MessageStateFailed:
- return tcell.ColorRed
- case ifc.MessageStateDefault:
- fallthrough
- default:
- return tcell.ColorDefault
+func (msg *TextMessage) getCache() tstring.TString {
+ if msg.cache == nil {
+ switch msg.MsgType {
+ case "m.emote":
+ msg.cache = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText), msg.TextColor())
+ msg.cache.Colorize(0, len(msg.MsgSender)+2, msg.SenderColor())
+ default:
+ msg.cache = tstring.NewColorTString(msg.MsgText, msg.TextColor())
+ }
}
+ return msg.cache
}
-// SenderColor returns the color the name of the sender should be shown in.
-//
-// If the message is being sent, the color is gray.
-// If sending has failed, the color is red.
-//
-// In any other case, the color is whatever is specified in the Message struct.
-// Usually that means it is the hash-based color of the sender (see ui/widget/color.go)
-func (msg *UITextMessage) SenderColor() tcell.Color {
- stateColor := msg.getStateSpecificColor()
- switch {
- case stateColor != tcell.ColorDefault:
- return stateColor
- case msg.MsgIsService:
- return tcell.ColorGray
- default:
- return msg.MsgSenderColor
- }
-}
+// CopyFrom replaces the content of this message object with the content of the given object.
+func (msg *TextMessage) CopyFrom(from ifc.MessageMeta) {
+ msg.BaseTextMessage.CopyFrom(from)
-// TextColor returns the color the actual content of the message should be shown in.
-func (msg *UITextMessage) TextColor() tcell.Color {
- stateColor := msg.getStateSpecificColor()
- switch {
- case stateColor != tcell.ColorDefault:
- return stateColor
- case msg.MsgIsService:
- return tcell.ColorGray
- case msg.MsgIsHighlight:
- return tcell.ColorYellow
- case msg.MsgType == "m.room.member":
- return tcell.ColorGreen
- default:
- return tcell.ColorDefault
+ fromTextMsg, ok := from.(*TextMessage)
+ if ok {
+ msg.MsgText = fromTextMsg.MsgText
}
-}
-
-// TimestampColor returns the color the timestamp should be shown in.
-//
-// As with SenderColor(), messages being sent and messages that failed to be sent are
-// gray and red respectively.
-//
-// However, other messages are the default color instead of a color stored in the struct.
-func (msg *UITextMessage) TimestampColor() tcell.Color {
- return msg.getStateSpecificColor()
-}
-
-// RecalculateBuffer calculates the buffer again with the previously provided width.
-func (msg *UITextMessage) RecalculateBuffer() {
- msg.CalculateBuffer(msg.prevBufferWidth)
-}
-
-// Buffer returns the computed text buffer.
-//
-// The buffer contains the text of the message split into lines with a maximum
-// width of whatever was provided to CalculateBuffer().
-//
-// N.B. This will NOT automatically calculate the buffer if it hasn't been
-// calculated already, as that requires the target width.
-func (msg *UITextMessage) Buffer() []tstring.TString {
- return msg.buffer
-}
-// Height returns the number of rows in the computed buffer (see Buffer()).
-func (msg *UITextMessage) Height() int {
- return len(msg.buffer)
+ msg.cache = nil
+ msg.RecalculateBuffer()
}
-
-// Timestamp returns the full timestamp when the message was sent.
-func (msg *UITextMessage) Timestamp() time.Time {
- return msg.MsgTimestamp
-}
-
-// FormatTime returns the formatted time when the message was sent.
-func (msg *UITextMessage) FormatTime() string {
- 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 *TextMessage) SetType(msgtype string) {
+ msg.BaseTextMessage.SetType(msgtype)
+ msg.cache = nil
}
-func (msg *UITextMessage) ID() string {
- return msg.MsgID
+func (msg *TextMessage) SetState(state ifc.MessageState) {
+ msg.BaseTextMessage.SetState(state)
+ msg.cache = nil
}
-func (msg *UITextMessage) SetID(id string) {
- msg.MsgID = id
+func (msg *TextMessage) SetIsHighlight(isHighlight bool) {
+ msg.BaseTextMessage.SetIsHighlight(isHighlight)
+ msg.cache = nil
}
-func (msg *UITextMessage) Type() string {
- return msg.MsgType
+func (msg *TextMessage) SetIsService(isService bool) {
+ msg.BaseTextMessage.SetIsService(isService)
+ msg.cache = nil
}
-func (msg *UITextMessage) SetType(msgtype string) {
- msg.MsgType = msgtype
- msg.text = nil
-}
-
-func (msg *UITextMessage) Text() string {
+func (msg *TextMessage) NotificationContent() string {
return msg.MsgText
}
-func (msg *UITextMessage) SetText(text string) {
- msg.MsgText = text
- msg.text = nil
-}
-
-func (msg *UITextMessage) State() ifc.MessageState {
- return msg.MsgState
+func (msg *TextMessage) CalculateBuffer(width int) {
+ msg.BaseTextMessage.calculateBufferWithText(msg.getCache(), width)
}
-func (msg *UITextMessage) SetState(state ifc.MessageState) {
- msg.MsgState = state
- msg.text = nil
-}
-
-func (msg *UITextMessage) IsHighlight() bool {
- return msg.MsgIsHighlight
-}
-
-func (msg *UITextMessage) SetIsHighlight(isHighlight bool) {
- msg.MsgIsHighlight = isHighlight
- msg.text = nil
-}
-
-func (msg *UITextMessage) IsService() bool {
- return msg.MsgIsService
-}
-
-func (msg *UITextMessage) SetIsService(isService bool) {
- msg.MsgIsService = isService
- msg.text = nil
-}
-
-func (msg *UITextMessage) GetTStringText() tstring.TString {
- if msg.text == nil || len(msg.text) == 0 {
- msg.text = tstring.NewColorTString(msg.Text(), msg.TextColor())
- }
- return msg.text
-}
-
-// Regular expressions used to split lines when calculating the buffer.
-//
-// From tview/textview.go
-var (
- boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
- spacePattern = regexp.MustCompile(`\s+`)
-)
-
-// CalculateBuffer generates the internal buffer for this message that consists
-// of the text of this message split into lines at most as wide as the width
-// parameter.
-func (msg *UITextMessage) CalculateBuffer(width int) {
- if width < 2 {
- return
- }
-
- msg.buffer = []tstring.TString{}
- text := msg.GetTStringText()
- if msg.MsgType == "m.emote" {
- text = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, text.String()), msg.TextColor())
- text.Colorize(0, len(msg.MsgSender), msg.SenderColor())
- }
-
- forcedLinebreaks := text.Split('\n')
- newlines := 0
- for _, str := range forcedLinebreaks {
- if len(str) == 0 && newlines < 1 {
- msg.buffer = append(msg.buffer, tstring.TString{})
- newlines++
- } else {
- newlines = 0
- }
- // Mostly from tview/textview.go#reindexBuffer()
- for len(str) > 0 {
- extract := str.Truncate(width)
- if len(extract) < len(str) {
- if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 {
- extract = str[:len(extract)+spaces[1]]
- }
-
- matches := boundaryPattern.FindAllStringIndex(extract.String(), -1)
- if len(matches) > 0 {
- extract = extract[:matches[len(matches)-1][1]]
- }
- }
- msg.buffer = append(msg.buffer, extract)
- str = str[len(extract):]
- }
- }
- msg.prevBufferWidth = width
+// RecalculateBuffer calculates the buffer again with the previously provided width.
+func (msg *TextMessage) RecalculateBuffer() {
+ msg.CalculateBuffer(msg.prevBufferWidth)
}
diff --git a/ui/view-main.go b/ui/view-main.go
index b21b3f4..e5850d3 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -134,12 +134,12 @@ func (view *MainView) InputSubmit(roomView *RoomView, text string) {
func (view *MainView) SendMessage(roomView *RoomView, text string) {
tempMessage := roomView.NewTempMessage("m.text", text)
- go view.sendTempMessage(roomView, tempMessage)
+ go view.sendTempMessage(roomView, tempMessage, text)
}
-func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message) {
+func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message, text string) {
defer view.gmx.Recover()
- eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type(), tempMessage.Text())
+ eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type(), text)
if err != nil {
tempMessage.SetState(ifc.MessageStateFailed)
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
@@ -153,8 +153,9 @@ func (view *MainView) HandleCommand(roomView *RoomView, command string, args []s
debug.Print("Handling command", command, args)
switch command {
case "/me":
- tempMessage := roomView.NewTempMessage("m.emote", strings.Join(args, " "))
- go view.sendTempMessage(roomView, tempMessage)
+ text := strings.Join(args, " ")
+ tempMessage := roomView.NewTempMessage("m.emote", text)
+ go view.sendTempMessage(roomView, tempMessage, text)
view.parent.Render()
case "/quit":
view.gmx.Stop()
@@ -411,7 +412,7 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
if shouldNotify && !isFocused {
// Push rules say notify and the terminal is not focused, send desktop notification.
shouldPlaySound := should.PlaySound && should.SoundName == "default"
- sendNotification(room, message.Sender(), message.Text(), should.Highlight, shouldPlaySound)
+ sendNotification(room, message.Sender(), message.NotificationContent(), should.Highlight, shouldPlaySound)
}
message.SetIsHighlight(should.Highlight)