Add support for explicit ordering and limiting rooms shown under a tag

Fixes #41
This commit is contained in:
Tulir Asokan 2018-05-04 00:55:28 +03:00
parent 93cbdc0ca8
commit 122b2441c9
4 changed files with 378 additions and 145 deletions

View File

@ -262,7 +262,11 @@ func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) {
index := 0 index := 0
for tag, infoifc := range tags { for tag, infoifc := range tags {
info, _ := infoifc.(map[string]interface{}) info, _ := infoifc.(map[string]interface{})
order, _ := info["order"].(float64) order := "0.5"
rawOrder, ok := info["order"]
if ok {
order = fmt.Sprintf("%v", rawOrder)
}
newTags[index] = rooms.RoomTag{ newTags[index] = rooms.RoomTag{
Tag: tag, Tag: tag,
Order: order, Order: order,

View File

@ -39,8 +39,8 @@ const (
type RoomTag struct { type RoomTag struct {
// The name of the tag. // The name of the tag.
Tag string Tag string
// The order of the tag. Smaller values are ordered higher. // The order of the tag.
Order float64 Order string
} }
// Room represents a single Matrix room. // Room represents a single Matrix room.
@ -118,7 +118,7 @@ func (room *Room) MarkRead() {
func (room *Room) Tags() []RoomTag { func (room *Room) Tags() []RoomTag {
if len(room.RawTags) == 0 { if len(room.RawTags) == 0 {
return []RoomTag{{"", 0.5}} return []RoomTag{{"", "0.5"}}
} }
return room.RawTags return room.RawTags
} }

View File

@ -21,7 +21,6 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
@ -30,9 +29,187 @@ import (
"maunium.net/go/tview" "maunium.net/go/tview"
) )
type roomListItem struct { type orderedRoom struct {
room *rooms.Room *rooms.Room
priority float64 order string
}
func newOrderedRoom(order string, room *rooms.Room) *orderedRoom {
return &orderedRoom{
Room: room,
order: order,
}
}
func convertRoom(room *rooms.Room) *orderedRoom {
return newOrderedRoom("", room)
}
func convertTaggedRoom(tag rooms.RoomTag, room *rooms.Room) *orderedRoom {
return newOrderedRoom(tag.Order, 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
}
func (trl *tagRoomList) Insert(order string, room *rooms.Room) {
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++ {
roomAtIndex := trl.rooms[i]
orderComp := strings.Compare(order, roomAtIndex.order)
if orderComp == 1 || (orderComp == 0 && roomAtIndex.LastReceivedMessage.After(room.LastReceivedMessage)) {
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] = newOrderedRoom(order, 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, "<<NIL>>")
} 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(room *rooms.Room) {
var found *orderedRoom
inserted := false
for i := 0; i < len(trl.rooms); i++ {
currentRoom := trl.rooms[i]
if found != nil {
if currentRoom.LastReceivedMessage.Before(room.LastReceivedMessage) {
trl.rooms[i-1] = currentRoom
} else {
trl.rooms[i-1] = found
inserted = true
break
}
} else if currentRoom.Room == room {
found = currentRoom
}
}
if !inserted {
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 { type RoomList struct {
@ -41,7 +218,7 @@ type RoomList struct {
// The list of tags in display order. // The list of tags in display order.
tags []string tags []string
// The list of rooms, in reverse order. // The list of rooms, in reverse order.
items map[string][]*rooms.Room items map[string]*tagRoomList
// The selected room. // The selected room.
selected *rooms.Room selected *rooms.Room
selectedTag string selectedTag string
@ -57,9 +234,9 @@ type RoomList struct {
} }
func NewRoomList() *RoomList { func NewRoomList() *RoomList {
return &RoomList{ list := &RoomList{
Box: tview.NewBox(), Box: tview.NewBox(),
items: make(map[string][]*rooms.Room), items: make(map[string]*tagRoomList),
tags: []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"}, tags: []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"},
scrollOffset: 0, scrollOffset: 0,
@ -68,11 +245,15 @@ func NewRoomList() *RoomList {
selectedTextColor: tcell.ColorWhite, selectedTextColor: tcell.ColorWhite,
selectedBackgroundColor: tcell.ColorDarkGreen, selectedBackgroundColor: tcell.ColorDarkGreen,
} }
for _, tag := range list.tags {
list.items[tag] = newTagRoomList()
}
return list
} }
func (list *RoomList) Contains(roomID string) bool { func (list *RoomList) Contains(roomID string) bool {
for _, roomList := range list.items { for _, tagRoomList := range list.items {
for _, room := range roomList { for _, room := range tagRoomList.All() {
if room.ID == roomID { if room.ID == roomID {
return true return true
} }
@ -83,16 +264,16 @@ func (list *RoomList) Contains(roomID string) bool {
func (list *RoomList) Add(room *rooms.Room) { func (list *RoomList) Add(room *rooms.Room) {
for _, tag := range room.Tags() { for _, tag := range room.Tags() {
list.AddToTag(tag.Tag, room) list.AddToTag(tag, room)
} }
} }
func (list *RoomList) CheckTag(tag string) { func (list *RoomList) CheckTag(tag string) {
index := list.IndexTag(tag) index := list.IndexTag(tag)
items, ok := list.items[tag] tagRoomList, ok := list.items[tag]
if ok && len(items) == 0 { if ok && tagRoomList.IsEmpty() {
delete(list.items, tag) delete(list.items, tag)
ok = false ok = false
} }
@ -105,37 +286,19 @@ func (list *RoomList) CheckTag(tag string) {
}*/ }*/
} }
func (list *RoomList) AddToTag(tag string, room *rooms.Room) { func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
if tag == "" && len(room.GetMembers()) == 2 { if tag.Tag == "" && len(room.GetMembers()) == 2 {
tag = "net.maunium.gomuks.fake.direct" tag.Tag = "net.maunium.gomuks.fake.direct"
} }
items, ok := list.items[tag]
tagRoomList, ok := list.items[tag.Tag]
if !ok { if !ok {
list.items[tag] = []*rooms.Room{room} list.items[tag.Tag] = newTagRoomList(convertRoom(room))
return return
} }
// Add space for new item. tagRoomList.Insert(tag.Order, room)
items = append(items, nil) list.CheckTag(tag.Tag)
// 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(items) - 1
// Find the spot where the new room should be put according to the last received message timestamps.
for i := 0; i < len(items)-1; i++ {
if items[i].LastReceivedMessage.After(room.LastReceivedMessage) {
insertAt = i
break
}
}
// Move newer rooms forward in the array.
for i := len(items) - 1; i > insertAt; i-- {
items[i] = items[i-1]
}
// Insert room.
items[insertAt] = room
list.items[tag] = items
list.CheckTag(tag)
} }
func (list *RoomList) Remove(room *rooms.Room) { func (list *RoomList) Remove(room *rooms.Room) {
@ -145,35 +308,32 @@ func (list *RoomList) Remove(room *rooms.Room) {
} }
func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) { func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
items, ok := list.items[tag] tagRoomList, ok := list.items[tag]
if !ok { if !ok {
return return
} }
index := list.indexInTag(tag, room) index := tagRoomList.Index(room)
if index == -1 { if index == -1 {
return return
} }
items = append(items[0:index], items[index+1:]...) tagRoomList.RemoveIndex(index)
if len(items) == 0 { if tagRoomList.IsEmpty() {
delete(list.items, tag) delete(list.items, tag)
} else {
list.items[tag] = items
} }
if room == list.selected { if room == list.selected {
// Room is currently selected, move selection to another room.
if index > 0 { if index > 0 {
list.selected = items[index-1] list.selected = tagRoomList.All()[index-1].Room
} else if len(items) > 0 { } else if tagRoomList.Length() > 0 {
list.selected = items[0] list.selected = tagRoomList.Visible()[0].Room
} else if len(list.items) > 0 { } else if len(list.items) > 0 {
for _, tag := range list.tags { for _, tag := range list.tags {
moreItems := list.items[tag] moreItems := list.items[tag]
if len(moreItems) > 0 { if moreItems.Length() > 0 {
list.selected = moreItems[0] list.selected = moreItems.Visible()[0].Room
list.selectedTag = tag list.selectedTag = tag
} }
} }
@ -187,33 +347,20 @@ func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
func (list *RoomList) Bump(room *rooms.Room) { func (list *RoomList) Bump(room *rooms.Room) {
for _, tag := range room.Tags() { for _, tag := range room.Tags() {
list.bumpInTag(tag.Tag, room) tagRoomList, ok := list.items[tag.Tag]
}
}
func (list *RoomList) bumpInTag(tag string, room *rooms.Room) {
items, ok := list.items[tag]
if !ok { if !ok {
return return
} }
tagRoomList.Bump(room)
found := false
for i := 0; i < len(items)-1; i++ {
if items[i] == room {
found = true
}
if found {
items[i] = items[i+1]
}
}
if found {
items[len(items)-1] = room
room.LastReceivedMessage = time.Now()
} }
} }
func (list *RoomList) Clear() { func (list *RoomList) Clear() {
list.items = make(map[string][]*rooms.Room) 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.selected = nil list.selected = nil
list.selectedTag = "" list.selectedTag = ""
} }
@ -224,9 +371,12 @@ func (list *RoomList) SetSelected(tag string, room *rooms.Room) {
pos := list.index(tag, room) pos := list.index(tag, room)
_, _, _, height := list.GetRect() _, _, _, height := list.GetRect()
if pos <= list.scrollOffset { if pos <= list.scrollOffset {
list.scrollOffset = pos-1 list.scrollOffset = pos - 1
} else if pos >= list.scrollOffset+height { } else if pos >= list.scrollOffset+height {
list.scrollOffset = pos-height+1 list.scrollOffset = pos - height + 1
}
if list.scrollOffset < 0 {
list.scrollOffset = 0
} }
debug.Print("Selecting", room.GetTitle(), "in", list.GetTagDisplayName(tag)) debug.Print("Selecting", room.GetTitle(), "in", list.GetTagDisplayName(tag))
} }
@ -245,21 +395,21 @@ func (list *RoomList) SelectedRoom() *rooms.Room {
func (list *RoomList) AddScrollOffset(offset int) { func (list *RoomList) AddScrollOffset(offset int) {
list.scrollOffset += offset list.scrollOffset += offset
if list.scrollOffset < 0 {
list.scrollOffset = 0
}
_, _, _, viewHeight := list.GetRect() _, _, _, viewHeight := list.GetRect()
contentHeight := list.ContentHeight() contentHeight := list.ContentHeight()
if list.scrollOffset > contentHeight-viewHeight { if list.scrollOffset > contentHeight-viewHeight {
list.scrollOffset = contentHeight - viewHeight list.scrollOffset = contentHeight - viewHeight
} }
if list.scrollOffset < 0 {
list.scrollOffset = 0
}
} }
func (list *RoomList) First() (string, *rooms.Room) { func (list *RoomList) First() (string, *rooms.Room) {
for _, tag := range list.tags { for _, tag := range list.tags {
items := list.items[tag] tagRoomList := list.items[tag]
if len(items) > 0 { if tagRoomList.HasVisibleRooms() {
return tag, items[len(items)-1] return tag, tagRoomList.FirstVisible()
} }
} }
return "", nil return "", nil
@ -268,9 +418,9 @@ func (list *RoomList) First() (string, *rooms.Room) {
func (list *RoomList) Last() (string, *rooms.Room) { func (list *RoomList) Last() (string, *rooms.Room) {
for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- { for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
tag := list.tags[tagIndex] tag := list.tags[tagIndex]
items := list.items[tag] tagRoomList := list.items[tag]
if len(items) > 0 { if tagRoomList.HasVisibleRooms() {
return tag, items[0] return tag, tagRoomList.LastVisible()
} }
} }
return "", nil return "", nil
@ -292,23 +442,28 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
return list.First() return list.First()
} }
items := list.items[list.selectedTag] tagRoomList := list.items[list.selectedTag]
index := list.indexInTag(list.selectedTag, list.selected) indexVisible := tagRoomList.IndexVisible(list.selected)
if index == -1 { index := tagRoomList.Index(list.selected)
return list.First()
} else if index == len(items)-1 { if indexVisible == tagRoomList.Length()-1 || (indexVisible == -1 && index == tagRoomList.TotalLength()-1) {
tagIndex := list.IndexTag(list.selectedTag) tagIndex := list.IndexTag(list.selectedTag)
tagIndex-- tagIndex--
for ; tagIndex >= 0; tagIndex-- { for ; tagIndex >= 0; tagIndex-- {
prevTag := list.tags[tagIndex] prevTag := list.tags[tagIndex]
prevTagItems := list.items[prevTag] prevTagRoomList := list.items[prevTag]
if len(prevTagItems) > 0 { if prevTagRoomList.HasVisibleRooms() {
return prevTag, prevTagItems[0] return prevTag, prevTagRoomList.LastVisible()
} }
} }
return list.Last() return list.Last()
} }
return list.selectedTag, items[index+1] if indexVisible != -1 {
return list.selectedTag, tagRoomList.Visible()[indexVisible+1].Room
} else if index != -1 {
return list.selectedTag, tagRoomList.All()[index+1].Room
}
return list.First()
} }
func (list *RoomList) Next() (string, *rooms.Room) { func (list *RoomList) Next() (string, *rooms.Room) {
@ -318,35 +473,28 @@ func (list *RoomList) Next() (string, *rooms.Room) {
return list.First() return list.First()
} }
items := list.items[list.selectedTag] tagRoomList := list.items[list.selectedTag]
index := list.indexInTag(list.selectedTag, list.selected) indexVisible := tagRoomList.IndexVisible(list.selected)
if index == -1 { index := tagRoomList.Index(list.selected)
return list.Last()
} else if index == 0 { if indexVisible == 0 || (indexVisible == -1 && index == 0) {
tagIndex := list.IndexTag(list.selectedTag) tagIndex := list.IndexTag(list.selectedTag)
tagIndex++ tagIndex++
for ; tagIndex < len(list.tags); tagIndex++ { for ; tagIndex < len(list.tags); tagIndex++ {
nextTag := list.tags[tagIndex] nextTag := list.tags[tagIndex]
nextTagItems := list.items[nextTag] nextTagRoomList := list.items[nextTag]
if len(nextTagItems) > 0 { if nextTagRoomList.HasVisibleRooms() {
return nextTag, nextTagItems[len(nextTagItems)-1] return nextTag, nextTagRoomList.FirstVisible()
} }
} }
return list.First() return list.First()
} }
return list.selectedTag, items[index-1] if indexVisible != -1 {
} return list.selectedTag, tagRoomList.Visible()[indexVisible-1].Room
} else if index != -1 {
func (list *RoomList) indexInTag(tag string, room *rooms.Room) int { return list.selectedTag, tagRoomList.All()[index-1].Room
roomIndex := -1
items := list.items[tag]
for index, entry := range items {
if entry == room {
roomIndex = index
break
} }
} return list.Last()
return roomIndex
} }
func (list *RoomList) index(tag string, room *rooms.Room) int { func (list *RoomList) index(tag string, room *rooms.Room) int {
@ -355,11 +503,15 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
return -1 return -1
} }
localIndex := list.indexInTag(tag, room) tagRoomList, ok := list.items[tag]
localIndex := -1
if ok {
localIndex = tagRoomList.IndexVisible(room)
}
if localIndex == -1 { if localIndex == -1 {
return -1 return -1
} }
localIndex = len(list.items[tag]) - 1 - localIndex localIndex = tagRoomList.Length() - 1 - localIndex
// Tag header // Tag header
localIndex += 1 localIndex += 1
@ -367,14 +519,22 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
if tagIndex > 0 { if tagIndex > 0 {
for i := 0; i < tagIndex; i++ { for i := 0; i < tagIndex; i++ {
previousTag := list.tags[i] previousTag := list.tags[i]
previousItems := list.items[previousTag] previousTagRoomList := list.items[previousTag]
tagDisplayName := list.GetTagDisplayName(previousTag) tagDisplayName := list.GetTagDisplayName(previousTag)
if len(tagDisplayName) > 0 { if len(tagDisplayName) > 0 {
if previousTagRoomList.IsCollapsed() {
localIndex++
continue
}
// Previous tag header + space // Previous tag header + space
localIndex += 2 localIndex += 2
if previousTagRoomList.HasInvisibleRooms() {
// Previous tag "Show more" button
localIndex++
}
// Previous tag items // Previous tag items
localIndex += len(previousItems) localIndex += previousTagRoomList.Length()
} }
} }
} }
@ -384,36 +544,70 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
func (list *RoomList) ContentHeight() (height int) { func (list *RoomList) ContentHeight() (height int) {
for _, tag := range list.tags { for _, tag := range list.tags {
items := list.items[tag] tagRoomList := list.items[tag]
tagDisplayName := list.GetTagDisplayName(tag) tagDisplayName := list.GetTagDisplayName(tag)
if len(tagDisplayName) == 0 { if len(tagDisplayName) == 0 {
continue continue
} }
height += 2 + len(items) if tagRoomList.IsCollapsed() {
height++
continue
}
height += 2 + tagRoomList.Length()
if tagRoomList.HasInvisibleRooms() {
height++
}
} }
return return
} }
func (list *RoomList) Get(n int) (string, *rooms.Room) { func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Room) {
n += list.scrollOffset line += list.scrollOffset
if n < 0 { if line < 0 {
return "", nil return "", nil
} }
for _, tag := range list.tags { for _, tag := range list.tags {
// Tag header tagRoomList := list.items[tag]
n-- if line--; line == -1 {
tagRoomList.ToggleCollapse()
items := list.items[tag]
if n < 0 {
return "", nil return "", nil
} else if n < len(items) { }
return tag, items[len(items)-1-n]
if tagRoomList.IsCollapsed() {
continue
}
if line < 0 {
return "", nil
} else if line < tagRoomList.Length() {
return tag, tagRoomList.Visible()[tagRoomList.Length()-1-line].Room
} }
// Tag items // Tag items
n -= len(items) line -= tagRoomList.Length()
hasMore := tagRoomList.HasInvisibleRooms()
hasLess := tagRoomList.maxShown > 10
if hasMore || hasLess {
if line--; line == -1 {
diff := 10
if mod {
diff = 100
}
_, _, width, _ := list.GetRect()
if column <= 6 && hasLess {
tagRoomList.maxShown -= diff
} else if column >= width-6 && hasMore {
tagRoomList.maxShown += diff
}
if tagRoomList.maxShown < 10 {
tagRoomList.maxShown = 10
}
return "", nil
}
}
// Tag footer // Tag footer
n-- line--
} }
return "", nil return "", nil
} }
@ -450,7 +644,7 @@ func (list *RoomList) Draw(screen tcell.Screen) {
// Draw the list items. // Draw the list items.
for _, tag := range list.tags { for _, tag := range list.tags {
items := list.items[tag] tagRoomList := list.items[tag]
tagDisplayName := list.GetTagDisplayName(tag) tagDisplayName := list.GetTagDisplayName(tag)
if len(tagDisplayName) == 0 { if len(tagDisplayName) == 0 {
continue continue
@ -459,8 +653,15 @@ func (list *RoomList) Draw(screen tcell.Screen) {
localOffset := 0 localOffset := 0
if handledOffset < list.scrollOffset { if handledOffset < list.scrollOffset {
if handledOffset+len(items) < list.scrollOffset { if handledOffset+tagRoomList.Length() < list.scrollOffset {
handledOffset += len(items) + 2 if tagRoomList.IsCollapsed() {
handledOffset++
} else {
handledOffset += tagRoomList.Length() + 2
if tagRoomList.HasInvisibleRooms() || tagRoomList.maxShown > 10 {
handledOffset++
}
}
continue continue
} else { } else {
localOffset = list.scrollOffset - handledOffset localOffset = list.scrollOffset - handledOffset
@ -469,8 +670,18 @@ func (list *RoomList) Draw(screen tcell.Screen) {
} }
widget.WriteLine(screen, tview.AlignLeft, tagDisplayName, x, y, width, tcell.StyleDefault.Underline(true).Bold(true)) widget.WriteLine(screen, tview.AlignLeft, tagDisplayName, x, y, width, tcell.StyleDefault.Underline(true).Bold(true))
items := tagRoomList.Visible()
if tagRoomList.IsCollapsed() {
screen.SetCell(x+width-1, y, tcell.StyleDefault, '▶')
y++ y++
for i := len(items) - 1; i >= 0; i-- { continue
}
screen.SetCell(x+width-1, y, tcell.StyleDefault, '▼')
y++
for i := tagRoomList.Length() - 1; i >= 0; i-- {
item := items[i] item := items[i]
index := len(items) - 1 - i index := len(items) - 1 - i
@ -487,7 +698,7 @@ func (list *RoomList) Draw(screen tcell.Screen) {
lineWidth := width lineWidth := width
style := tcell.StyleDefault.Foreground(list.mainTextColor) style := tcell.StyleDefault.Foreground(list.mainTextColor)
if tag == list.selectedTag && item == list.selected { if tag == list.selectedTag && item.Room == list.selected {
style = style.Foreground(list.selectedTextColor).Background(list.selectedBackgroundColor) style = style.Foreground(list.selectedTextColor).Background(list.selectedBackgroundColor)
} }
if item.HasNewMessages { if item.HasNewMessages {
@ -508,12 +719,24 @@ func (list *RoomList) Draw(screen tcell.Screen) {
} }
widget.WriteLine(screen, tview.AlignLeft, text, x, y, lineWidth, style) widget.WriteLine(screen, tview.AlignLeft, text, x, y, lineWidth, style)
y++ y++
if y >= bottomLimit { if y >= bottomLimit {
break break
} }
} }
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++
}
y++ y++
} }
} }

View File

@ -263,8 +263,13 @@ func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMo
view.parent.Render() view.parent.Render()
case tcell.Button1: case tcell.Button1:
_, rly, _, _ := msgView.GetRect() _, rly, _, _ := msgView.GetRect()
n := y - rly + 1 line := y - rly + 1
view.SwitchRoom(view.roomList.Get(n)) switchToTag, switchToRoom := view.roomList.HandleClick(x, line, event.Modifiers() == tcell.ModCtrl)
if switchToRoom != nil {
view.SwitchRoom(switchToTag, switchToRoom)
} else {
view.parent.Render()
}
} }
default: default:
debug.Print("Unhandled mouse event:", event.Buttons(), event.Modifiers(), x, y) debug.Print("Unhandled mouse event:", event.Buttons(), event.Modifiers(), x, y)
@ -386,11 +391,11 @@ func (view *MainView) UpdateTags(room *rooms.Room, newTags []rooms.RoomTag) {
for _, tag := range room.RawTags { for _, tag := range room.RawTags {
view.roomList.RemoveFromTag(tag.Tag, room) view.roomList.RemoveFromTag(tag.Tag, room)
} }
view.roomList.AddToTag("", room) view.roomList.AddToTag(rooms.RoomTag{Tag: "", Order: "0.5"}, room)
} else if len(room.RawTags) == 0 { } else if len(room.RawTags) == 0 {
view.roomList.RemoveFromTag("", room) view.roomList.RemoveFromTag("", room)
for _, tag := range newTags { for _, tag := range newTags {
view.roomList.AddToTag(tag.Tag, room) view.roomList.AddToTag(tag, room)
} }
} else { } else {
NewTags: NewTags:
@ -400,7 +405,7 @@ func (view *MainView) UpdateTags(room *rooms.Room, newTags []rooms.RoomTag) {
continue NewTags continue NewTags
} }
} }
view.roomList.AddToTag(newTag.Tag, room) view.roomList.AddToTag(newTag, room)
} }
OldTags: OldTags:
for _, oldTag := range room.RawTags { for _, oldTag := range room.RawTags {
@ -456,6 +461,7 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
} }
message.SetIsHighlight(should.Highlight) message.SetIsHighlight(should.Highlight)
room.LastReceivedMessage = message.Timestamp()
view.roomList.Bump(room) view.roomList.Bump(room)
} }