From 14903e0cdcd3ba78face2cbe0ad0287da269a1ea Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 May 2018 22:06:48 +0300 Subject: Add bare mode and fix terminal resize bug. Fixes #48 --- ui/message-view.go | 51 ++++++++++++++++++++++++-------------- ui/messages/base.go | 3 +++ ui/messages/expandedtextmessage.go | 14 +++++++---- ui/messages/imagemessage.go | 18 +++++++++++--- ui/messages/message.go | 2 +- ui/messages/parser/parser.go | 4 +-- ui/messages/textbase.go | 39 ++++++++++++++--------------- ui/messages/textmessage.go | 22 +++++++++------- ui/room-list.go | 2 +- ui/room-view.go | 39 ++++++++++++++++------------- ui/view-main.go | 42 +++++++++++++++++++++++++------ 11 files changed, 152 insertions(+), 84 deletions(-) (limited to 'ui') diff --git a/ui/message-view.go b/ui/message-view.go index f2fb72f..6ee92f5 100644 --- a/ui/message-view.go +++ b/ui/message-view.go @@ -50,6 +50,7 @@ type MessageView struct { prevWidth int prevHeight int prevMsgCount int + prevBareMode bool messageIDs map[string]messages.UIMessage messages []messages.UIMessage @@ -76,6 +77,7 @@ func NewMessageView(parent *RoomView) *MessageView { prevWidth: -1, prevHeight: -1, prevMsgCount: -1, + prevBareMode: false, } } @@ -168,9 +170,12 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag view.updateWidestSender(message.Sender()) - _, _, width, _ := view.GetInnerRect() - width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap - message.CalculateBuffer(width) + _, _, width, _ := view.GetRect() + bare := view.parent.parent.bareDisplay + if !bare { + width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap + } + message.CalculateBuffer(bare, width) if direction == ifc.AppendMessage { if view.ScrollOffset > 0 { @@ -258,11 +263,13 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages } func (view *MessageView) recalculateBuffers() { - _, _, width, height := view.GetInnerRect() - - width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap - recalculateMessageBuffers := width != view.prevWidth - if height != view.prevHeight || recalculateMessageBuffers || len(view.messages) != view.prevMsgCount { + _, _, width, height := view.GetRect() + bareMode := view.parent.parent.bareDisplay + if !bareMode { + width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap + } + recalculateMessageBuffers := width != view.prevWidth || bareMode != view.prevBareMode + if recalculateMessageBuffers || len(view.messages) != view.prevMsgCount { view.textBuffer = []tstring.TString{} view.metaBuffer = []ifc.MessageMeta{} view.prevMsgCount = 0 @@ -272,13 +279,14 @@ func (view *MessageView) recalculateBuffers() { break } if recalculateMessageBuffers { - message.CalculateBuffer(width) + message.CalculateBuffer(bareMode, width) } view.appendBuffer(message) } - view.prevHeight = height - view.prevWidth = width } + view.prevHeight = height + view.prevWidth = width + view.prevBareMode = bareMode } func (view *MessageView) handleMessageClick(message ifc.MessageMeta) bool { @@ -362,7 +370,7 @@ func (view *MessageView) HandleClick(x, y int, button tcell.ButtonMask) bool { const PaddingAtTop = 5 func (view *MessageView) AddScrollOffset(diff int) { - _, _, _, height := view.GetInnerRect() + _, _, _, height := view.GetRect() totalHeight := view.TotalHeight() if diff >= 0 && view.ScrollOffset+diff >= totalHeight-height+PaddingAtTop { @@ -380,7 +388,7 @@ func (view *MessageView) AddScrollOffset(diff int) { } func (view *MessageView) Height() int { - _, _, _, height := view.GetInnerRect() + _, _, _, height := view.GetRect() return height } @@ -389,7 +397,7 @@ func (view *MessageView) TotalHeight() int { } func (view *MessageView) IsAtTop() bool { - _, _, _, height := view.GetInnerRect() + _, _, _, height := view.GetRect() totalHeight := len(view.textBuffer) return view.ScrollOffset >= totalHeight-height+PaddingAtTop } @@ -442,14 +450,14 @@ func (view *MessageView) getIndexOffset(screen tcell.Screen, height, messageX in if view.LoadingMessages { message = "Loading more messages..." } - _, y, _, _ := view.GetInnerRect() + _, y, _, _ := view.GetRect() widget.WriteLineSimpleColor(screen, message, messageX, y, tcell.ColorGreen) } return } func (view *MessageView) Draw(screen tcell.Screen) { - x, y, _, height := view.GetInnerRect() + x, y, _, height := view.GetRect() view.recalculateBuffers() if view.TotalHeight() == 0 { @@ -461,6 +469,11 @@ func (view *MessageView) Draw(screen tcell.Screen) { messageX := usernameX + view.widestSender + SenderMessageGap separatorX := usernameX + view.widestSender + SenderSeparatorGap + bareMode := view.parent.parent.bareDisplay + if bareMode { + messageX = 0 + } + indexOffset := view.getIndexOffset(screen, height, messageX) if len(view.textBuffer) != len(view.metaBuffer) { @@ -491,14 +504,16 @@ func (view *MessageView) Draw(screen tcell.Screen) { firstLine = false - screen.SetContent(separatorX, y+line, borderChar, nil, borderStyle) + if !bareMode { + screen.SetContent(separatorX, y+line, borderChar, nil, borderStyle) + } text, meta := view.textBuffer[index], view.metaBuffer[index] if meta != prevMeta { if len(meta.FormatTime()) > 0 { widget.WriteLineSimpleColor(screen, meta.FormatTime(), x, y+line, meta.TimestampColor()) } - if prevMeta == nil || meta.Sender() != prevMeta.Sender() { + if !bareMode && (prevMeta == nil || meta.Sender() != prevMeta.Sender()) { widget.WriteLineColor( screen, tview.AlignRight, meta.Sender(), usernameX, y+line, view.widestSender, diff --git a/ui/messages/base.go b/ui/messages/base.go index d6ff4f6..ae37431 100644 --- a/ui/messages/base.go +++ b/ui/messages/base.go @@ -41,7 +41,9 @@ type BaseMessage struct { MsgIsHighlight bool MsgIsService bool buffer []tstring.TString + plainBuffer []tstring.TString prevBufferWidth int + prevBareMode bool } func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseMessage { @@ -53,6 +55,7 @@ func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time MsgType: msgtype, MsgID: id, prevBufferWidth: 0, + prevBareMode: false, MsgState: ifc.MessageStateDefault, MsgIsHighlight: false, MsgIsService: false, diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go index 38e0afd..6534b04 100644 --- a/ui/messages/expandedtextmessage.go +++ b/ui/messages/expandedtextmessage.go @@ -28,14 +28,14 @@ func init() { } type ExpandedTextMessage struct { - BaseTextMessage + BaseMessage MsgText tstring.TString } // NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state. func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage { return &ExpandedTextMessage{ - BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp), + BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), MsgText: text, } } @@ -48,11 +48,15 @@ func (msg *ExpandedTextMessage) NotificationContent() string { return msg.MsgText.String() } -func (msg *ExpandedTextMessage) CalculateBuffer(width int) { - msg.BaseTextMessage.calculateBufferWithText(msg.MsgText, width) +func (msg *ExpandedTextMessage) PlainText() string { + return msg.MsgText.String() +} + +func (msg *ExpandedTextMessage) CalculateBuffer(bare bool, width int) { + msg.calculateBufferWithText(bare, msg.MsgText, width) } // RecalculateBuffer calculates the buffer again with the previously provided width. func (msg *ExpandedTextMessage) RecalculateBuffer() { - msg.CalculateBuffer(msg.prevBufferWidth) + msg.CalculateBuffer(msg.prevBareMode, msg.prevBufferWidth) } diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go index 04103b4..3f50193 100644 --- a/ui/messages/imagemessage.go +++ b/ui/messages/imagemessage.go @@ -37,6 +37,7 @@ func init() { type ImageMessage struct { BaseMessage + Body string Homeserver string FileID string data []byte @@ -45,9 +46,10 @@ type ImageMessage struct { } // NewImageMessage creates a new ImageMessage object with the provided values and the default state. -func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage { +func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname, msgtype, body, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage { return &ImageMessage{ newBaseMessage(id, sender, displayname, msgtype, timestamp), + body, homeserver, fileID, data, @@ -67,6 +69,10 @@ func (msg *ImageMessage) NotificationContent() string { return "Sent an image" } +func (msg *ImageMessage) PlainText() string { + return fmt.Sprintf("%s: %s", msg.Body, msg.matrix.GetDownloadURL(msg.Homeserver, msg.FileID)) +} + func (msg *ImageMessage) updateData() { defer debug.Recover() debug.Print("Loading image:", msg.Homeserver, msg.FileID) @@ -86,11 +92,16 @@ func (msg *ImageMessage) Path() string { // 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 *ImageMessage) CalculateBuffer(width int) { +func (msg *ImageMessage) CalculateBuffer(bare bool, width int) { if width < 2 { return } + if bare { + msg.calculateBufferWithText(bare, tstring.NewTString(msg.PlainText()), width) + return + } + image, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.data), 0, width, color.Black) if err != nil { msg.buffer = []tstring.TString{tstring.NewColorTString("Failed to display image", tcell.ColorRed)} @@ -100,9 +111,10 @@ func (msg *ImageMessage) CalculateBuffer(width int) { msg.buffer = image.Render() msg.prevBufferWidth = width + msg.prevBareMode = false } // RecalculateBuffer calculates the buffer again with the previously provided width. func (msg *ImageMessage) RecalculateBuffer() { - msg.CalculateBuffer(msg.prevBufferWidth) + msg.CalculateBuffer(msg.prevBareMode, msg.prevBufferWidth) } diff --git a/ui/messages/message.go b/ui/messages/message.go index 790ae8b..98a84e0 100644 --- a/ui/messages/message.go +++ b/ui/messages/message.go @@ -25,7 +25,7 @@ import ( type UIMessage interface { ifc.Message - CalculateBuffer(width int) + CalculateBuffer(bare bool, width int) RecalculateBuffer() Buffer() []tstring.TString Height() int diff --git a/ui/messages/parser/parser.go b/ui/messages/parser/parser.go index 19c86e7..d06223a 100644 --- a/ui/messages/parser/parser.go +++ b/ui/messages/parser/parser.go @@ -56,6 +56,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Ev displayname = member.DisplayName } msgtype, _ := evt.Content["msgtype"].(string) + text, _ := evt.Content["body"].(string) ts := unixToTime(evt.Timestamp) switch msgtype { case "m.text", "m.notice", "m.emote": @@ -64,7 +65,6 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Ev text := ParseHTMLMessage(room, evt, displayname) return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) } - text, _ := evt.Content["body"].(string) text = strings.Replace(text, "\t", " ", -1) return messages.NewTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) case "m.image": @@ -73,7 +73,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Ev if err != nil { debug.Printf("Failed to download %s: %v", url, err) } - return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, msgtype, hs, id, data, ts) + return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, msgtype, text, hs, id, data, ts) } return nil } diff --git a/ui/messages/textbase.go b/ui/messages/textbase.go index f805067..faf53e6 100644 --- a/ui/messages/textbase.go +++ b/ui/messages/textbase.go @@ -17,35 +17,26 @@ package messages import ( - "encoding/gob" "regexp" - "time" - "maunium.net/go/gomuks/ui/messages/tstring" + "fmt" ) -func init() { - gob.Register(BaseTextMessage{}) -} - -type BaseTextMessage struct { - BaseMessage -} - -func newBaseTextMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseTextMessage { - return BaseTextMessage{newBaseMessage(id, sender, displayname, msgtype, timestamp)} -} - // Regular expressions used to split lines when calculating the buffer. // // From tview/textview.go var ( - boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)") + boundaryPattern = regexp.MustCompile(`([[:punct:]]\s*|\s+)`) + bareBoundaryPattern = regexp.MustCompile(`(\s+)`) spacePattern = regexp.MustCompile(`\s+`) ) -func matchBoundaryPattern(extract tstring.TString) tstring.TString { - matches := boundaryPattern.FindAllStringIndex(extract.String(), -1) +func matchBoundaryPattern(bare bool, extract tstring.TString) tstring.TString { + regex := boundaryPattern + if bare { + regex = bareBoundaryPattern + } + matches := regex.FindAllStringIndex(extract.String(), -1) if len(matches) > 0 { if match := matches[len(matches)-1]; len(match) >= 2 { if until := match[1]; until < len(extract) { @@ -59,13 +50,20 @@ func matchBoundaryPattern(extract tstring.TString) tstring.TString { // 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) { +func (msg *BaseMessage) calculateBufferWithText(bare bool, text tstring.TString, width int) { if width < 2 { return } msg.buffer = []tstring.TString{} + if bare { + text = tstring. + NewTString(msg.FormatTime()). + AppendTString(tstring.NewColorTString(fmt.Sprintf(" <%s> ", msg.Sender()), msg.SenderColor())). + AppendTString(text) + } + forcedLinebreaks := text.Split('\n') newlines := 0 for _, str := range forcedLinebreaks { @@ -82,11 +80,12 @@ func (msg *BaseTextMessage) calculateBufferWithText(text tstring.TString, width if spaces := spacePattern.FindStringIndex(str[len(extract):].String()); spaces != nil && spaces[0] == 0 { extract = str[:len(extract)+spaces[1]] } - extract = matchBoundaryPattern(extract) + extract = matchBoundaryPattern(bare, extract) } msg.buffer = append(msg.buffer, extract) str = str[len(extract):] } } msg.prevBufferWidth = width + msg.prevBareMode = bare } diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go index 5139e07..20dcbfc 100644 --- a/ui/messages/textmessage.go +++ b/ui/messages/textmessage.go @@ -30,7 +30,7 @@ func init() { } type TextMessage struct { - BaseTextMessage + BaseMessage cache tstring.TString MsgText string } @@ -38,7 +38,7 @@ type TextMessage struct { // NewTextMessage creates a new UITextMessage object with the provided values and the default state. func NewTextMessage(id, sender, displayname, msgtype, text string, timestamp time.Time) UIMessage { return &TextMessage{ - BaseTextMessage: newBaseTextMessage(id, sender, displayname, msgtype, timestamp), + BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), MsgText: text, } } @@ -57,22 +57,22 @@ func (msg *TextMessage) getCache() tstring.TString { } func (msg *TextMessage) SetType(msgtype string) { - msg.BaseTextMessage.SetType(msgtype) + msg.BaseMessage.SetType(msgtype) msg.cache = nil } func (msg *TextMessage) SetState(state ifc.MessageState) { - msg.BaseTextMessage.SetState(state) + msg.BaseMessage.SetState(state) msg.cache = nil } func (msg *TextMessage) SetIsHighlight(isHighlight bool) { - msg.BaseTextMessage.SetIsHighlight(isHighlight) + msg.BaseMessage.SetIsHighlight(isHighlight) msg.cache = nil } func (msg *TextMessage) SetIsService(isService bool) { - msg.BaseTextMessage.SetIsService(isService) + msg.BaseMessage.SetIsService(isService) msg.cache = nil } @@ -80,11 +80,15 @@ func (msg *TextMessage) NotificationContent() string { return msg.MsgText } -func (msg *TextMessage) CalculateBuffer(width int) { - msg.BaseTextMessage.calculateBufferWithText(msg.getCache(), width) +func (msg *TextMessage) PlainText() string { + return msg.MsgText +} + +func (msg *TextMessage) CalculateBuffer(bare bool, width int) { + msg.calculateBufferWithText(bare, msg.getCache(), width) } // RecalculateBuffer calculates the buffer again with the previously provided width. func (msg *TextMessage) RecalculateBuffer() { - msg.CalculateBuffer(msg.prevBufferWidth) + msg.CalculateBuffer(msg.prevBareMode, msg.prevBufferWidth) } diff --git a/ui/room-list.go b/ui/room-list.go index e5b4d4e..a7543a9 100644 --- a/ui/room-list.go +++ b/ui/room-list.go @@ -426,7 +426,7 @@ func (list *RoomList) GetTagDisplayName(tag string) string { func (list *RoomList) Draw(screen tcell.Screen) { list.Box.Draw(screen) - x, y, width, height := list.GetInnerRect() + x, y, width, height := list.GetRect() yLimit := y + height y -= list.scrollOffset diff --git a/ui/room-view.go b/ui/room-view.go index 5f9a8de..e39506a 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -147,23 +147,11 @@ func (view *RoomView) Focus(delegate func(p tview.Primitive)) { delegate(view.input) } -// Constants defining the size of the room view grid. -const ( - UserListBorderWidth = 1 - UserListWidth = 20 - StaticHorizontalSpace = UserListBorderWidth + UserListWidth - - TopicBarHeight = 1 - StatusBarHeight = 1 - InputBarHeight = 1 - StaticVerticalSpace = TopicBarHeight + StatusBarHeight + InputBarHeight -) - func (view *RoomView) GetStatus() string { var buf strings.Builder if len(view.completions.list) > 0 { - if view.completions.textCache != view.input.GetText() || view.completions.time.Add(10*time.Second).Before(time.Now()) { + if view.completions.textCache != view.input.GetText() || view.completions.time.Add(10 * time.Second).Before(time.Now()) { view.completions.list = []string{} } else { buf.WriteString(strings.Join(view.completions.list, ", ")) @@ -184,11 +172,23 @@ func (view *RoomView) GetStatus() string { } func (view *RoomView) Draw(screen tcell.Screen) { - x, y, width, height := view.GetInnerRect() + x, y, width, height := view.GetRect() if width <= 0 || height <= 0 { return } + // Constants defining the size of the room view grid. + const ( + UserListBorderWidth = 1 + UserListWidth = 20 + StaticHorizontalSpace = UserListBorderWidth + UserListWidth + + TopicBarHeight = 1 + StatusBarHeight = 1 + InputBarHeight = 1 + StaticVerticalSpace = TopicBarHeight + StatusBarHeight + InputBarHeight + ) + // Calculate actual grid based on view rectangle and constants defined above. var ( contentHeight = height - StaticVerticalSpace @@ -202,12 +202,15 @@ func (view *RoomView) Draw(screen tcell.Screen) { statusRow = contentRow + contentHeight inputRow = statusRow + StatusBarHeight ) + if !view.parent.ShowUserList() { + contentWidth = width + } // Update the rectangles of all the children. view.topic.SetRect(x, topicRow, width, TopicBarHeight) view.content.SetRect(x, contentRow, contentWidth, contentHeight) view.status.SetRect(x, statusRow, width, StatusBarHeight) - if userListColumn > x { + if view.parent.ShowUserList() && userListColumn > x { view.userList.SetRect(userListColumn, contentRow, UserListWidth, contentHeight) view.ulBorder.SetRect(userListBorderColumn, contentRow, UserListBorderWidth, contentHeight) } @@ -220,8 +223,10 @@ func (view *RoomView) Draw(screen tcell.Screen) { view.status.SetText(view.GetStatus()) view.status.Draw(screen) view.input.Draw(screen) - view.ulBorder.Draw(screen) - view.userList.Draw(screen) + if view.parent.ShowUserList() { + view.ulBorder.Draw(screen) + view.userList.Draw(screen) + } } func (view *RoomView) SetCompletions(completions []string) { diff --git a/ui/view-main.go b/ui/view-main.go index 74a6ce1..bda5d80 100644 --- a/ui/view-main.go +++ b/ui/view-main.go @@ -48,6 +48,18 @@ type MainView struct { gmx ifc.Gomuks config *config.Config parent *GomuksUI + + hideUserList bool + hideRoomList bool + bareDisplay bool +} + +func (view *MainView) ShowRoomList() bool { + return !view.bareDisplay && !view.hideRoomList +} + +func (view *MainView) ShowUserList() bool { + return !view.bareDisplay && !view.hideUserList } func (ui *GomuksUI) NewMainView() tview.Primitive { @@ -63,10 +75,11 @@ func (ui *GomuksUI) NewMainView() tview.Primitive { parent: ui, } - mainView.SetDirection(tview.FlexColumn) - mainView.AddItem(mainView.roomList, 25, 0, false) - mainView.AddItem(widget.NewBorder(), 1, 0, false) - mainView.AddItem(mainView.roomView, 0, 1, true) + mainView. + SetDirection(tview.FlexColumn). + AddItem(mainView.roomList, 25, 0, false). + AddItem(widget.NewBorder(), 1, 0, false). + AddItem(mainView.roomView, 0, 1, true) mainView.BumpFocus(nil) ui.mainView = mainView @@ -74,6 +87,15 @@ func (ui *GomuksUI) NewMainView() tview.Primitive { return mainView } +func (view *MainView) Draw(screen tcell.Screen) { + if !view.ShowRoomList() { + view.roomView.SetRect(view.GetRect()) + view.roomView.Draw(screen) + } else { + view.Flex.Draw(screen) + } +} + func (view *MainView) BumpFocus(roomView *RoomView) { view.lastFocusTime = time.Now() view.MarkRead(roomView) @@ -195,16 +217,20 @@ func (view *MainView) KeyEventHandler(roomView *RoomView, key *tcell.EventKey) * view.BumpFocus(roomView) k := key.Key() + c := key.Rune() if key.Modifiers() == tcell.ModCtrl || key.Modifiers() == tcell.ModAlt { - switch k { - case tcell.KeyDown: + switch { + case k == tcell.KeyDown: view.SwitchRoom(view.roomList.Next()) - case tcell.KeyUp: + case k == tcell.KeyUp: view.SwitchRoom(view.roomList.Previous()) - case tcell.KeyEnter: + case k == tcell.KeyEnter: searchModal := NewFuzzySearchModal(view, 42, 12) view.parent.views.AddPage("fuzzy-search-modal", searchModal, true, true) view.parent.app.SetFocus(searchModal) + case c == 'l': + view.bareDisplay = !view.bareDisplay + view.parent.Render() default: return key } -- cgit v1.2.3