From f5e07f40db87b221fafd081bda9801abd3c86376 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 May 2018 18:30:25 +0300 Subject: Move TagRoomList stuff into new file and split RoomList.Draw() --- ui/room-list.go | 452 ++++++++++---------------------------------------------- 1 file changed, 78 insertions(+), 374 deletions(-) (limited to 'ui/room-list.go') diff --git a/ui/room-list.go b/ui/room-list.go index 75d874f..5c40c44 100644 --- a/ui/room-list.go +++ b/ui/room-list.go @@ -17,210 +17,23 @@ package ui import ( - "fmt" "regexp" - "strconv" "strings" "math" "maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/matrix/rooms" - "maunium.net/go/gomuks/ui/widget" "maunium.net/go/tcell" "maunium.net/go/tview" ) -type orderedRoom struct { - *rooms.Room - order string -} - -func newOrderedRoom(order string, room *rooms.Room) *orderedRoom { - return &orderedRoom{ - Room: room, - order: order, - } -} - -func convertRoom(room *rooms.Room) *orderedRoom { - return newOrderedRoom("0.5", room) -} - -type tagRoomList struct { - rooms []*orderedRoom - maxShown int -} - -func newTagRoomList(rooms ...*orderedRoom) *tagRoomList { - return &tagRoomList{ - maxShown: 10, - rooms: rooms, - } -} - -func (trl *tagRoomList) Visible() []*orderedRoom { - return trl.rooms[len(trl.rooms)-trl.Length():] -} - -func (trl *tagRoomList) FirstVisible() *rooms.Room { - visible := trl.Visible() - if len(visible) > 0 { - return visible[len(visible)-1].Room - } - return nil -} - -func (trl *tagRoomList) LastVisible() *rooms.Room { - visible := trl.Visible() - if len(visible) > 0 { - return visible[0].Room - } - return nil -} - -func (trl *tagRoomList) All() []*orderedRoom { - return trl.rooms -} - -func (trl *tagRoomList) Length() int { - if len(trl.rooms) < trl.maxShown { - return len(trl.rooms) - } - return trl.maxShown -} - -func (trl *tagRoomList) TotalLength() int { - return len(trl.rooms) -} - -func (trl *tagRoomList) IsEmpty() bool { - return len(trl.rooms) == 0 -} - -func (trl *tagRoomList) IsCollapsed() bool { - return trl.maxShown == 0 -} - -func (trl *tagRoomList) ToggleCollapse() { - if trl.IsCollapsed() { - trl.maxShown = 10 - } else { - trl.maxShown = 0 - } -} - -func (trl *tagRoomList) HasInvisibleRooms() bool { - return trl.maxShown < trl.TotalLength() -} - -func (trl *tagRoomList) HasVisibleRooms() bool { - return !trl.IsEmpty() && trl.maxShown > 0 -} - -// ShouldBeBefore returns if the first room should be after the second room in the room list. -// The manual order and last received message timestamp are considered. -func (trl *tagRoomList) ShouldBeAfter(room1 *orderedRoom, room2 *orderedRoom) bool { - orderComp := strings.Compare(room1.order, room2.order) - return orderComp == 1 || (orderComp == 0 && room2.LastReceivedMessage.After(room1.LastReceivedMessage)) -} - -func (trl *tagRoomList) Insert(order string, mxRoom *rooms.Room) { - room := newOrderedRoom(order, mxRoom) - trl.rooms = append(trl.rooms, nil) - // The default insert index is the newly added slot. - // That index will be used if all other rooms in the list have the same LastReceivedMessage timestamp. - insertAt := len(trl.rooms) - 1 - // Find the spot where the new room should be put according to the last received message timestamps. - for i := 0; i < len(trl.rooms)-1; i++ { - if trl.ShouldBeAfter(room, trl.rooms[i]) { - insertAt = i - break - } - } - // Move newer rooms forward in the array. - for i := len(trl.rooms) - 1; i > insertAt; i-- { - trl.rooms[i] = trl.rooms[i-1] - } - // Insert room. - trl.rooms[insertAt] = room -} - -func (trl *tagRoomList) String() string { - var str strings.Builder - fmt.Fprintln(&str, "&tagRoomList{") - fmt.Fprintf(&str, " maxShown: %d,\n", trl.maxShown) - fmt.Fprint(&str, " rooms: {") - for i, room := range trl.rooms { - if room == nil { - fmt.Fprintf(&str, "<>") - } else { - fmt.Fprint(&str, room.ID) - } - if i != len(trl.rooms)-1 { - fmt.Fprint(&str, ", ") - } - } - fmt.Fprintln(&str, "},") - fmt.Fprintln(&str, "}") - return str.String() -} - -func (trl *tagRoomList) Bump(mxRoom *rooms.Room) { - var found *orderedRoom - for i := 0; i < len(trl.rooms); i++ { - currentRoom := trl.rooms[i] - if found != nil { - if trl.ShouldBeAfter(found, trl.rooms[i]) { - // This room should be after the room being bumped, so insert the - // room being bumped here and return - trl.rooms[i-1] = found - return - } - // Move older rooms back in the array - trl.rooms[i-1] = currentRoom - } else if currentRoom.Room == mxRoom { - found = currentRoom - } - } - // If the room being bumped should be first in the list, it won't be inserted during the loop. - trl.rooms[len(trl.rooms)-1] = found -} - -func (trl *tagRoomList) Remove(room *rooms.Room) { - trl.RemoveIndex(trl.Index(room)) -} - -func (trl *tagRoomList) RemoveIndex(index int) { - if index < 0 || index > len(trl.rooms) { - return - } - trl.rooms = append(trl.rooms[0:index], trl.rooms[index+1:]...) -} - -func (trl *tagRoomList) Index(room *rooms.Room) int { - return trl.indexInList(trl.All(), room) -} - -func (trl *tagRoomList) IndexVisible(room *rooms.Room) int { - return trl.indexInList(trl.Visible(), room) -} - -func (trl *tagRoomList) indexInList(list []*orderedRoom, room *rooms.Room) int { - for index, entry := range list { - if entry.Room == room { - return index - } - } - return -1 -} - type RoomList struct { *tview.Box // The list of tags in display order. tags []string // The list of rooms, in reverse order. - items map[string]*tagRoomList + items map[string]*TagRoomList // The selected room. selected *rooms.Room selectedTag string @@ -238,7 +51,7 @@ type RoomList struct { func NewRoomList() *RoomList { list := &RoomList{ Box: tview.NewBox(), - items: make(map[string]*tagRoomList), + items: make(map[string]*TagRoomList), tags: []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"}, scrollOffset: 0, @@ -248,14 +61,14 @@ func NewRoomList() *RoomList { selectedBackgroundColor: tcell.ColorDarkGreen, } for _, tag := range list.tags { - list.items[tag] = newTagRoomList() + list.items[tag] = NewTagRoomList(list, tag) } return list } func (list *RoomList) Contains(roomID string) bool { - for _, tagRoomList := range list.items { - for _, room := range tagRoomList.All() { + for _, trl := range list.items { + for _, room := range trl.All() { if room.ID == roomID { return true } @@ -274,9 +87,9 @@ func (list *RoomList) Add(room *rooms.Room) { func (list *RoomList) CheckTag(tag string) { index := list.IndexTag(tag) - tagRoomList, ok := list.items[tag] + trl, ok := list.items[tag] - if ok && tagRoomList.IsEmpty() { + if ok && trl.IsEmpty() { //delete(list.items, tag) ok = false } @@ -290,13 +103,13 @@ func (list *RoomList) CheckTag(tag string) { } func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) { - tagRoomList, ok := list.items[tag.Tag] + trl, ok := list.items[tag.Tag] if !ok { - list.items[tag.Tag] = newTagRoomList(convertRoom(room)) + list.items[tag.Tag] = NewTagRoomList(list, tag.Tag, convertRoom(room)) return } - tagRoomList.Insert(tag.Order, room) + trl.Insert(tag.Order, room) list.CheckTag(tag.Tag) } @@ -307,27 +120,27 @@ func (list *RoomList) Remove(room *rooms.Room) { } func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) { - tagRoomList, ok := list.items[tag] + trl, ok := list.items[tag] if !ok { return } - index := tagRoomList.Index(room) + index := trl.Index(room) if index == -1 { return } - tagRoomList.RemoveIndex(index) + trl.RemoveIndex(index) - if tagRoomList.IsEmpty() { + if trl.IsEmpty() { // delete(list.items, tag) } if room == list.selected { if index > 0 { - list.selected = tagRoomList.All()[index-1].Room - } else if tagRoomList.Length() > 0 { - list.selected = tagRoomList.Visible()[0].Room + list.selected = trl.All()[index-1].Room + } else if trl.Length() > 0 { + list.selected = trl.Visible()[0].Room } else if len(list.items) > 0 { for _, tag := range list.tags { moreItems := list.items[tag] @@ -346,19 +159,19 @@ func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) { func (list *RoomList) Bump(room *rooms.Room) { for _, tag := range room.Tags() { - tagRoomList, ok := list.items[tag.Tag] + trl, ok := list.items[tag.Tag] if !ok { return } - tagRoomList.Bump(room) + trl.Bump(room) } } func (list *RoomList) Clear() { - list.items = make(map[string]*tagRoomList) + list.items = make(map[string]*TagRoomList) list.tags = []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"} for _, tag := range list.tags { - list.items[tag] = newTagRoomList() + list.items[tag] = NewTagRoomList(list, tag) } list.selected = nil list.selectedTag = "" @@ -406,9 +219,9 @@ func (list *RoomList) AddScrollOffset(offset int) { func (list *RoomList) First() (string, *rooms.Room) { for _, tag := range list.tags { - tagRoomList := list.items[tag] - if tagRoomList.HasVisibleRooms() { - return tag, tagRoomList.FirstVisible() + trl := list.items[tag] + if trl.HasVisibleRooms() { + return tag, trl.FirstVisible() } } return "", nil @@ -417,9 +230,9 @@ func (list *RoomList) First() (string, *rooms.Room) { func (list *RoomList) Last() (string, *rooms.Room) { for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- { tag := list.tags[tagIndex] - tagRoomList := list.items[tag] - if tagRoomList.HasVisibleRooms() { - return tag, tagRoomList.LastVisible() + trl := list.items[tag] + if trl.HasVisibleRooms() { + return tag, trl.LastVisible() } } return "", nil @@ -441,28 +254,28 @@ func (list *RoomList) Previous() (string, *rooms.Room) { return list.First() } - tagRoomList := list.items[list.selectedTag] - index := tagRoomList.IndexVisible(list.selected) - indexInvisible := tagRoomList.Index(list.selected) + trl := list.items[list.selectedTag] + index := trl.IndexVisible(list.selected) + indexInvisible := trl.Index(list.selected) if index == -1 && indexInvisible >= 0 { - num := tagRoomList.TotalLength() - indexInvisible - tagRoomList.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0) - index = tagRoomList.IndexVisible(list.selected) + num := trl.TotalLength() - indexInvisible + trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0) + index = trl.IndexVisible(list.selected) } - if index == tagRoomList.Length()-1 { + if index == trl.Length()-1 { tagIndex := list.IndexTag(list.selectedTag) tagIndex-- for ; tagIndex >= 0; tagIndex-- { prevTag := list.tags[tagIndex] - prevTagRoomList := list.items[prevTag] - if prevTagRoomList.HasVisibleRooms() { - return prevTag, prevTagRoomList.LastVisible() + prevTRL := list.items[prevTag] + if prevTRL.HasVisibleRooms() { + return prevTag, prevTRL.LastVisible() } } return list.Last() } else if index >= 0 { - return list.selectedTag, tagRoomList.Visible()[index+1].Room + return list.selectedTag, trl.Visible()[index+1].Room } return list.First() } @@ -474,13 +287,13 @@ func (list *RoomList) Next() (string, *rooms.Room) { return list.First() } - tagRoomList := list.items[list.selectedTag] - index := tagRoomList.IndexVisible(list.selected) - indexInvisible := tagRoomList.Index(list.selected) + trl := list.items[list.selectedTag] + index := trl.IndexVisible(list.selected) + indexInvisible := trl.Index(list.selected) if index == -1 && indexInvisible >= 0 { - num := tagRoomList.TotalLength() - indexInvisible + 1 - tagRoomList.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0) - index = tagRoomList.IndexVisible(list.selected) + num := trl.TotalLength() - indexInvisible + 1 + trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0) + index = trl.IndexVisible(list.selected) } if index == 0 { @@ -488,14 +301,14 @@ func (list *RoomList) Next() (string, *rooms.Room) { tagIndex++ for ; tagIndex < len(list.tags); tagIndex++ { nextTag := list.tags[tagIndex] - nextTagRoomList := list.items[nextTag] - if nextTagRoomList.HasVisibleRooms() { - return nextTag, nextTagRoomList.FirstVisible() + nextTRL := list.items[nextTag] + if nextTRL.HasVisibleRooms() { + return nextTag, nextTRL.FirstVisible() } } return list.First() } else if index > 0 { - return list.selectedTag, tagRoomList.Visible()[index-1].Room + return list.selectedTag, trl.Visible()[index-1].Room } return list.Last() } @@ -506,39 +319,24 @@ func (list *RoomList) index(tag string, room *rooms.Room) int { return -1 } - tagRoomList, ok := list.items[tag] + trl, ok := list.items[tag] localIndex := -1 if ok { - localIndex = tagRoomList.IndexVisible(room) + localIndex = trl.IndexVisible(room) } if localIndex == -1 { return -1 } - localIndex = tagRoomList.Length() - 1 - localIndex + localIndex = trl.Length() - 1 - localIndex // Tag header localIndex += 1 if tagIndex > 0 { for i := 0; i < tagIndex; i++ { - previousTag := list.tags[i] - previousTagRoomList := list.items[previousTag] - - tagDisplayName := list.GetTagDisplayName(previousTag) - if len(tagDisplayName) > 0 { - if previousTagRoomList.IsCollapsed() { - localIndex++ - continue - } - // Previous tag header + space - localIndex += 2 - if previousTagRoomList.HasInvisibleRooms() { - // Previous tag "Show more" button - localIndex++ - } - // Previous tag items - localIndex += previousTagRoomList.Length() - } + prevTag := list.tags[i] + prevTRL := list.items[prevTag] + localIndex += prevTRL.RenderHeight() } } @@ -547,19 +345,7 @@ func (list *RoomList) index(tag string, room *rooms.Room) int { func (list *RoomList) ContentHeight() (height int) { for _, tag := range list.tags { - tagRoomList := list.items[tag] - tagDisplayName := list.GetTagDisplayName(tag) - if len(tagDisplayName) == 0 { - continue - } - if tagRoomList.IsCollapsed() { - height++ - continue - } - height += 2 + tagRoomList.Length() - if tagRoomList.HasInvisibleRooms() { - height++ - } + height += list.items[tag].RenderHeight() } return } @@ -570,27 +356,27 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro return "", nil } for _, tag := range list.tags { - tagRoomList := list.items[tag] + trl := list.items[tag] if line--; line == -1 { - tagRoomList.ToggleCollapse() + trl.ToggleCollapse() return "", nil } - if tagRoomList.IsCollapsed() { + if trl.IsCollapsed() { continue } if line < 0 { return "", nil - } else if line < tagRoomList.Length() { - return tag, tagRoomList.Visible()[tagRoomList.Length()-1-line].Room + } else if line < trl.Length() { + return tag, trl.Visible()[trl.Length()-1-line].Room } // Tag items - line -= tagRoomList.Length() + line -= trl.Length() - hasMore := tagRoomList.HasInvisibleRooms() - hasLess := tagRoomList.maxShown > 10 + hasMore := trl.HasInvisibleRooms() + hasLess := trl.maxShown > 10 if hasMore || hasLess { if line--; line == -1 { diff := 10 @@ -599,12 +385,12 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro } _, _, width, _ := list.GetRect() if column <= 6 && hasLess { - tagRoomList.maxShown -= diff + trl.maxShown -= diff } else if column >= width-6 && hasMore { - tagRoomList.maxShown += diff + trl.maxShown += diff } - if tagRoomList.maxShown < 10 { - tagRoomList.maxShown = 10 + if trl.maxShown < 10 { + trl.maxShown = 10 } return "", nil } @@ -641,108 +427,26 @@ func (list *RoomList) Draw(screen tcell.Screen) { list.Box.Draw(screen) x, y, width, height := list.GetInnerRect() - bottomLimit := y + height - - handledOffset := 0 + yLimit := y + height + y -= list.scrollOffset // Draw the list items. for _, tag := range list.tags { - tagRoomList := list.items[tag] + trl := list.items[tag] tagDisplayName := list.GetTagDisplayName(tag) if len(tagDisplayName) == 0 { continue } - localOffset := 0 - - if handledOffset < list.scrollOffset { - if handledOffset+tagRoomList.Length() < list.scrollOffset { - if tagRoomList.IsCollapsed() { - handledOffset++ - } else { - handledOffset += tagRoomList.Length() + 2 - if tagRoomList.HasInvisibleRooms() || tagRoomList.maxShown > 10 { - handledOffset++ - } - } - continue - } else { - localOffset = list.scrollOffset - handledOffset - handledOffset += localOffset - } - } - - roomCount := strconv.Itoa(tagRoomList.TotalLength()) - widget.WriteLine(screen, tview.AlignLeft, tagDisplayName, x, y, width-1-len(roomCount), tcell.StyleDefault.Underline(true).Bold(true)) - widget.WriteLine(screen, tview.AlignLeft, roomCount, x+len(tagDisplayName)+1, y, width-2-len(tagDisplayName), tcell.StyleDefault.Italic(true)) - - items := tagRoomList.Visible() - - if tagRoomList.IsCollapsed() { - screen.SetCell(x+width-1, y, tcell.StyleDefault, '▶') - y++ - continue - } - screen.SetCell(x+width-1, y, tcell.StyleDefault, '▼') - y++ - - for i := tagRoomList.Length() - 1; i >= 0; i-- { - item := items[i] - index := len(items) - 1 - i - - if y >= bottomLimit { - break - } - - if index < localOffset { - continue - } - - text := item.GetTitle() - - lineWidth := width - - style := tcell.StyleDefault.Foreground(list.mainTextColor) - if tag == list.selectedTag && item.Room == list.selected { - style = style.Foreground(list.selectedTextColor).Background(list.selectedBackgroundColor) - } - if item.HasNewMessages() { - style = style.Bold(true) - } - - unreadCount := item.UnreadCount() - if unreadCount > 0 { - unreadMessageCount := "99+" - if unreadCount < 100 { - unreadMessageCount = strconv.Itoa(unreadCount) - } - if item.Highlighted() { - unreadMessageCount += "!" - } - unreadMessageCount = fmt.Sprintf("(%s)", unreadMessageCount) - widget.WriteLine(screen, tview.AlignRight, unreadMessageCount, x+lineWidth-7, y, 7, style) - lineWidth -= len(unreadMessageCount) - } - - widget.WriteLinePadded(screen, tview.AlignLeft, text, x, y, lineWidth, style) - y++ - - if y >= bottomLimit { - break - } + renderHeight := trl.RenderHeight() + if y + renderHeight >= yLimit { + renderHeight = yLimit - y } - hasLess := tagRoomList.maxShown > 10 - hasMore := tagRoomList.HasInvisibleRooms() - if hasLess || hasMore { - if hasMore { - widget.WriteLine(screen, tview.AlignRight, "More ↓", x, y, width, tcell.StyleDefault) - } - if hasLess { - widget.WriteLine(screen, tview.AlignLeft, "↑ Less", x, y, width, tcell.StyleDefault) - } - y++ + trl.SetRect(x, y, width, renderHeight) + trl.Draw(screen) + y += renderHeight + if y >= yLimit { + break } - - y++ } } -- cgit v1.2.3