mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-03-25 11:29:33 +00:00
fixed backlog
This commit is contained in:
parent
6d9f82a351
commit
f185882de9
internal/ui
messages
primitives/completion
|
@ -40,7 +40,6 @@ type Container interface {
|
||||||
CreateMessageUnsafe(cchat.MessageCreate)
|
CreateMessageUnsafe(cchat.MessageCreate)
|
||||||
UpdateMessageUnsafe(cchat.MessageUpdate)
|
UpdateMessageUnsafe(cchat.MessageUpdate)
|
||||||
DeleteMessageUnsafe(cchat.MessageDelete)
|
DeleteMessageUnsafe(cchat.MessageDelete)
|
||||||
PrependMessageUnsafe(cchat.MessageCreate)
|
|
||||||
|
|
||||||
// FirstMessage returns the first message in the buffer. Nil is returned if
|
// FirstMessage returns the first message in the buffer. Nil is returned if
|
||||||
// there's nothing.
|
// there's nothing.
|
||||||
|
@ -107,22 +106,9 @@ func (c *GridContainer) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
||||||
c.GridStore.CreateMessageUnsafe(msg)
|
c.GridStore.CreateMessageUnsafe(msg)
|
||||||
|
|
||||||
// Determine if the user is scrolled to the bottom for cleaning up.
|
// Determine if the user is scrolled to the bottom for cleaning up.
|
||||||
if !c.Bottomed() {
|
if c.Bottomed() {
|
||||||
return
|
// Clean up the backlog.
|
||||||
}
|
c.DeleteEarliest(c.MessagesLen() - BacklogLimit)
|
||||||
|
|
||||||
// Clean up the backlog.
|
|
||||||
if clean := len(c.messages) - BacklogLimit; clean > 0 {
|
|
||||||
// Remove them from the map and the container.
|
|
||||||
for _, id := range c.messageIDs[:clean] {
|
|
||||||
delete(c.messages, id)
|
|
||||||
// We can gradually pop the first item off here, as we're removing
|
|
||||||
// from 0th, and items are being shifted backwards.
|
|
||||||
c.Grid.RemoveRow(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cut the message IDs away by shifting the slice.
|
|
||||||
c.messageIDs = append(c.messageIDs[:0], c.messageIDs[clean:]...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +123,3 @@ func (c *GridContainer) UpdateMessage(msg cchat.MessageUpdate) {
|
||||||
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
|
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
|
||||||
gts.ExecAsync(func() { c.DeleteMessageUnsafe(msg) })
|
gts.ExecAsync(func() { c.DeleteMessageUnsafe(msg) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GridContainer) PrependMessage(msg cchat.MessageCreate) {
|
|
||||||
gts.ExecAsync(func() { c.PrependMessageUnsafe(msg) })
|
|
||||||
}
|
|
||||||
|
|
|
@ -137,6 +137,17 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||||
c.compact(c.GridContainer.LastMessage())
|
c.compact(c.GridContainer.LastMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if we need to collapse the second message.
|
||||||
|
if sec := c.NthMessage(1); sec != nil {
|
||||||
|
// If the author isn't the same, then ignore.
|
||||||
|
if sec.AuthorID() != msg.Author().ID() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The author is the same; collapse.
|
||||||
|
c.compact(sec)
|
||||||
|
}
|
||||||
|
|
||||||
// Did the handler wipe old messages? It will only do so if the user is
|
// Did the handler wipe old messages? It will only do so if the user is
|
||||||
// scrolled to the bottom.
|
// scrolled to the bottom.
|
||||||
if !c.Bottomed() {
|
if !c.Bottomed() {
|
||||||
|
@ -154,8 +165,7 @@ func (c *Container) DeleteMessage(msg cchat.MessageDelete) {
|
||||||
gts.ExecAsync(func() {
|
gts.ExecAsync(func() {
|
||||||
// Get the previous and next message before deleting. We'll need them to
|
// Get the previous and next message before deleting. We'll need them to
|
||||||
// evaluate whether we need to change anything.
|
// evaluate whether we need to change anything.
|
||||||
prev := c.GridStore.Before(msg.ID())
|
prev, next := c.GridStore.Around(msg.ID())
|
||||||
next := c.GridStore.After(msg.ID())
|
|
||||||
|
|
||||||
// The function doesn't actually try and re-collapse the bottom message
|
// The function doesn't actually try and re-collapse the bottom message
|
||||||
// when a sandwiched message is deleted. This is fine.
|
// when a sandwiched message is deleted. This is fine.
|
||||||
|
@ -211,23 +221,6 @@ func (c *Container) uncompact(msg container.GridMessage) {
|
||||||
c.GridStore.SwapMessage(full)
|
c.GridStore.SwapMessage(full)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) PrependMessage(msg cchat.MessageCreate) {
|
|
||||||
gts.ExecAsync(func() {
|
|
||||||
c.GridContainer.PrependMessageUnsafe(msg)
|
|
||||||
|
|
||||||
// See if we need to uncollapse the second message.
|
|
||||||
if sec := c.NthMessage(1); sec != nil {
|
|
||||||
// If the author isn't the same, then ignore.
|
|
||||||
if sec.AuthorID() != msg.Author().ID() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The author is the same; collapse.
|
|
||||||
c.compact(sec)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) compact(msg container.GridMessage) {
|
func (c *Container) compact(msg container.GridMessage) {
|
||||||
// Exit if the message is already collapsed.
|
// Exit if the message is already collapsed.
|
||||||
if collapse, ok := msg.(Collapsible); !ok || collapse.Collapsed() {
|
if collapse, ok := msg.(Collapsible); !ok || collapse.Collapsed() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"container/list"
|
||||||
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||||
|
@ -17,8 +17,8 @@ type GridStore struct {
|
||||||
Construct Constructor
|
Construct Constructor
|
||||||
Controller Controller
|
Controller Controller
|
||||||
|
|
||||||
messages map[string]*gridMessage
|
messages map[string]*gridMessage
|
||||||
messageIDs []string // ids or nonces
|
messageList *list.List
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGridStore(constr Constructor, ctrl Controller) *GridStore {
|
func NewGridStore(constr Constructor, ctrl Controller) *GridStore {
|
||||||
|
@ -32,15 +32,16 @@ func NewGridStore(constr Constructor, ctrl Controller) *GridStore {
|
||||||
primitives.AddClass(grid, "message-grid")
|
primitives.AddClass(grid, "message-grid")
|
||||||
|
|
||||||
return &GridStore{
|
return &GridStore{
|
||||||
Grid: grid,
|
Grid: grid,
|
||||||
Construct: constr,
|
Construct: constr,
|
||||||
Controller: ctrl,
|
Controller: ctrl,
|
||||||
messages: map[string]*gridMessage{},
|
messages: map[string]*gridMessage{},
|
||||||
|
messageList: list.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GridStore) MessagesLen() int {
|
func (c *GridStore) MessagesLen() int {
|
||||||
return len(c.messages)
|
return c.messageList.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GridStore) attachGrid(row int, widgets []gtk.IWidget) {
|
func (c *GridStore) attachGrid(row int, widgets []gtk.IWidget) {
|
||||||
|
@ -49,14 +50,21 @@ func (c *GridStore) attachGrid(row int, widgets []gtk.IWidget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// findIndex searches backwards for idnonce.
|
func (c *GridStore) findElement(id cchat.ID) (*list.Element, *gridMessage, int) {
|
||||||
func (c *GridStore) findIndex(idnonce string) int {
|
var index = c.messageList.Len() - 1
|
||||||
for i := len(c.messageIDs) - 1; i >= 0; i-- {
|
for elem := c.messageList.Back(); elem != nil; elem = elem.Prev() {
|
||||||
if c.messageIDs[i] == idnonce {
|
if gridMsg := elem.Value.(*gridMessage); gridMsg.ID() == id {
|
||||||
return i
|
return elem, gridMsg, index
|
||||||
}
|
}
|
||||||
|
index--
|
||||||
}
|
}
|
||||||
return -1
|
return nil, nil, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// findIndex searches backwards for id.
|
||||||
|
func (c *GridStore) findIndex(id cchat.ID) (*gridMessage, int) {
|
||||||
|
_, gridMsg, ix := c.findElement(id)
|
||||||
|
return gridMsg, ix
|
||||||
}
|
}
|
||||||
|
|
||||||
type CoordinateTranslator interface {
|
type CoordinateTranslator interface {
|
||||||
|
@ -66,12 +74,11 @@ type CoordinateTranslator interface {
|
||||||
var _ CoordinateTranslator = (*gtk.Widget)(nil)
|
var _ CoordinateTranslator = (*gtk.Widget)(nil)
|
||||||
|
|
||||||
func (c *GridStore) TranslateCoordinates(parent gtk.IWidget, msg GridMessage) (y int) {
|
func (c *GridStore) TranslateCoordinates(parent gtk.IWidget, msg GridMessage) (y int) {
|
||||||
i := c.findIndex(msg.ID())
|
m, i := c.findIndex(msg.ID())
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
m, _ := c.messages[c.messageIDs[i]]
|
|
||||||
w, _ := m.Focusable().(CoordinateTranslator)
|
w, _ := m.Focusable().(CoordinateTranslator)
|
||||||
|
|
||||||
// x is not needed.
|
// x is not needed.
|
||||||
|
@ -81,9 +88,6 @@ func (c *GridStore) TranslateCoordinates(parent gtk.IWidget, msg GridMessage) (y
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.Println("X:", x)
|
|
||||||
// log.Println("Y:", y)
|
|
||||||
|
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,16 +96,16 @@ func (c *GridStore) TranslateCoordinates(parent gtk.IWidget, msg GridMessage) (y
|
||||||
//
|
//
|
||||||
// TODO: combine compact and full so they share the same attach method.
|
// TODO: combine compact and full so they share the same attach method.
|
||||||
func (c *GridStore) SwapMessage(msg GridMessage) bool {
|
func (c *GridStore) SwapMessage(msg GridMessage) bool {
|
||||||
// Get the current message's index.
|
// Wrap msg inside a *gridMessage if it's not already.
|
||||||
var ix = c.findIndex(msg.ID())
|
m, ok := msg.(*gridMessage)
|
||||||
if ix == -1 {
|
if !ok {
|
||||||
return false
|
m = &gridMessage{GridMessage: msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap msg inside a *gridMessage if it's not already.
|
// Get the current message's index.
|
||||||
mg, ok := msg.(*gridMessage)
|
_, ix := c.findIndex(msg.ID())
|
||||||
if !ok {
|
if ix == -1 {
|
||||||
mg = &gridMessage{GridMessage: msg}
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a row at index. The actual row we want to delete will be shifted
|
// Add a row at index. The actual row we want to delete will be shifted
|
||||||
|
@ -109,10 +113,10 @@ func (c *GridStore) SwapMessage(msg GridMessage) bool {
|
||||||
c.Grid.InsertRow(ix)
|
c.Grid.InsertRow(ix)
|
||||||
|
|
||||||
// Let the new message be attached on top of the to-be-replaced message.
|
// Let the new message be attached on top of the to-be-replaced message.
|
||||||
c.attachGrid(ix, mg.Attach())
|
c.attachGrid(ix, m.Attach())
|
||||||
|
|
||||||
// Set the message into the map.
|
// Set the message into the map.
|
||||||
c.messages[mg.ID()] = mg
|
c.messages[m.ID()] = m
|
||||||
|
|
||||||
// Delete the to-be-replaced message, which we have shifted downwards
|
// Delete the to-be-replaced message, which we have shifted downwards
|
||||||
// earlier, so we add 1.
|
// earlier, so we add 1.
|
||||||
|
@ -121,29 +125,40 @@ func (c *GridStore) SwapMessage(msg GridMessage) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before returns the message before the given ID, or nil if none.
|
// Around returns the message before and after the given ID, or nil if none.
|
||||||
func (c *GridStore) Before(id string) GridMessage {
|
func (c *GridStore) Around(id cchat.ID) (before, after GridMessage) {
|
||||||
return c.getOffsetted(id, -1)
|
gridBefore, gridAfter := c.around(id)
|
||||||
}
|
|
||||||
|
|
||||||
// After returns the message after the given ID, or nil if none.
|
if gridBefore != nil {
|
||||||
func (c *GridStore) After(id string) GridMessage {
|
before = gridBefore.GridMessage
|
||||||
return c.getOffsetted(id, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GridStore) getOffsetted(id string, offset int) GridMessage {
|
|
||||||
// Get the current index.
|
|
||||||
var ix = c.findIndex(id)
|
|
||||||
if ix == -1 {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
ix += offset
|
if gridAfter != nil {
|
||||||
|
after = gridAfter.GridMessage
|
||||||
if ix < 0 || ix >= len(c.messages) {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.messages[c.messageIDs[ix]].GridMessage
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GridStore) around(id cchat.ID) (before, after *gridMessage) {
|
||||||
|
var last *gridMessage
|
||||||
|
var next bool
|
||||||
|
|
||||||
|
for elem := c.messageList.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
message := elem.Value.(*gridMessage)
|
||||||
|
if next {
|
||||||
|
after = message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if message.ID() == id {
|
||||||
|
// The last message is the before.
|
||||||
|
before = last
|
||||||
|
next = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
last = message
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestMessageFrom returns the latest message with the given user ID. This is
|
// LatestMessageFrom returns the latest message with the given user ID. This is
|
||||||
|
@ -164,25 +179,30 @@ func (c *GridStore) LatestMessageFrom(userID string) (msgID string, ok bool) {
|
||||||
// FindMessage iterates backwards and returns the message if isMessage() returns
|
// FindMessage iterates backwards and returns the message if isMessage() returns
|
||||||
// true on that message.
|
// true on that message.
|
||||||
func (c *GridStore) FindMessage(isMessage func(msg GridMessage) bool) GridMessage {
|
func (c *GridStore) FindMessage(isMessage func(msg GridMessage) bool) GridMessage {
|
||||||
for i := len(c.messageIDs) - 1; i >= 0; i-- {
|
for elem := c.messageList.Back(); elem != nil; elem = elem.Prev() {
|
||||||
msg := c.messages[c.messageIDs[i]]
|
gridMsg := elem.Value.(*gridMessage)
|
||||||
// Ignore sending messages.
|
// Ignore sending messages.
|
||||||
if msg.presend != nil {
|
if gridMsg.presend != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check.
|
if gridMsg := gridMsg.GridMessage; isMessage(gridMsg) {
|
||||||
if msg := msg.GridMessage; isMessage(msg) {
|
return gridMsg
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NthMessage returns the nth message.
|
// NthMessage returns the nth message.
|
||||||
func (c *GridStore) NthMessage(n int) GridMessage {
|
func (c *GridStore) NthMessage(n int) GridMessage {
|
||||||
if len(c.messageIDs) > 0 && n >= 0 && n < len(c.messageIDs) {
|
var index = 0
|
||||||
return c.messages[c.messageIDs[n]].GridMessage
|
for elem := c.messageList.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
if index == n {
|
||||||
|
return elem.Value.(*gridMessage).GridMessage
|
||||||
|
}
|
||||||
|
index++
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,13 +246,6 @@ func (c *GridStore) message(msgID cchat.ID, nonce string) *gridMessage {
|
||||||
// Destroy the presend struct.
|
// Destroy the presend struct.
|
||||||
m.presend = nil
|
m.presend = nil
|
||||||
|
|
||||||
// Replace the nonce inside the ID slice with the actual ID.
|
|
||||||
if ix := c.findIndex(nonce); ix > -1 {
|
|
||||||
c.messageIDs[ix] = msgID
|
|
||||||
} else {
|
|
||||||
log.Error(fmt.Errorf("Missed ID %s in slice index %d", msgID, ix))
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,33 +265,14 @@ func (c *GridStore) AddPresendMessage(msg input.PresendMessage) PresendGridMessa
|
||||||
|
|
||||||
// Set the message into the grid.
|
// Set the message into the grid.
|
||||||
c.attachGrid(c.MessagesLen(), msgc.Attach())
|
c.attachGrid(c.MessagesLen(), msgc.Attach())
|
||||||
// Append the NONCE.
|
// Append the message.
|
||||||
c.messageIDs = append(c.messageIDs, msgc.Nonce())
|
c.messageList.PushBack(msgc)
|
||||||
// Set the NONCE into the message map.
|
// Set the NONCE into the message map.
|
||||||
c.messages[msgc.Nonce()] = msgc
|
c.messages[msgc.Nonce()] = msgc
|
||||||
|
|
||||||
return presend
|
return presend
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GridStore) PrependMessageUnsafe(msg cchat.MessageCreate) {
|
|
||||||
msgc := &gridMessage{
|
|
||||||
GridMessage: c.Construct.NewMessage(msg),
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Grid.InsertRow(0)
|
|
||||||
c.attachGrid(0, msgc.Attach())
|
|
||||||
|
|
||||||
// Prepend the message ID.
|
|
||||||
c.messageIDs = append(c.messageIDs, "")
|
|
||||||
copy(c.messageIDs[1:], c.messageIDs)
|
|
||||||
c.messageIDs[0] = msgc.ID()
|
|
||||||
|
|
||||||
// Set the message into the map.
|
|
||||||
c.messages[msgc.ID()] = msgc
|
|
||||||
|
|
||||||
c.Controller.BindMenu(msgc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GridStore) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
func (c *GridStore) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
||||||
// Call the event handler last.
|
// Call the event handler last.
|
||||||
defer c.Controller.AuthorEvent(msg.Author())
|
defer c.Controller.AuthorEvent(msg.Author())
|
||||||
|
@ -296,11 +290,36 @@ func (c *GridStore) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
||||||
msgc := &gridMessage{
|
msgc := &gridMessage{
|
||||||
GridMessage: c.Construct.NewMessage(msg),
|
GridMessage: c.Construct.NewMessage(msg),
|
||||||
}
|
}
|
||||||
|
msgTime := msg.Time()
|
||||||
|
|
||||||
// Copy from PresendMessage.
|
var index = c.messageList.Len() - 1
|
||||||
c.attachGrid(c.MessagesLen(), msgc.Attach())
|
var after = c.messageList.Back()
|
||||||
c.messageIDs = append(c.messageIDs, msgc.ID())
|
|
||||||
c.messages[msgc.ID()] = msgc
|
// Iterate and compare timestamp to find where to insert a message.
|
||||||
|
for after != nil {
|
||||||
|
if msgTime.After(after.Value.(*gridMessage).Time()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index--
|
||||||
|
after = after.Prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the message. If after is nil, then that means the message is the
|
||||||
|
// oldest, so we add it to the front of the list.
|
||||||
|
if after != nil {
|
||||||
|
index++ // insert right after
|
||||||
|
c.messageList.InsertAfter(msgc, after)
|
||||||
|
} else {
|
||||||
|
index = 0
|
||||||
|
c.messageList.PushFront(msgc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the message into the grid.
|
||||||
|
c.Grid.InsertRow(index)
|
||||||
|
c.attachGrid(index, msgc.Attach())
|
||||||
|
|
||||||
|
// Set the NONCE into the message map.
|
||||||
|
c.messages[msgc.Nonce()] = msgc
|
||||||
|
|
||||||
c.Controller.BindMenu(msgc)
|
c.Controller.BindMenu(msgc)
|
||||||
}
|
}
|
||||||
|
@ -326,22 +345,41 @@ func (c *GridStore) DeleteMessageUnsafe(msg cchat.MessageDelete) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopMessage deletes a message off of the list and return the deleted message.
|
// PopMessage deletes a message off of the list and return the deleted message.
|
||||||
func (c *GridStore) PopMessage(id string) (msg GridMessage) {
|
func (c *GridStore) PopMessage(id cchat.ID) (msg GridMessage) {
|
||||||
// Search for the index.
|
// Get the raw element to delete it off the list.
|
||||||
var ix = c.findIndex(id)
|
elem, gridMsg, ix := c.findElement(id)
|
||||||
if ix < 0 {
|
if elem == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
msg = gridMsg.GridMessage
|
||||||
// Grab the message before deleting.
|
|
||||||
msg = c.messages[id]
|
|
||||||
|
|
||||||
// Remove off of the Gtk grid.
|
// Remove off of the Gtk grid.
|
||||||
c.Grid.RemoveRow(ix)
|
c.Grid.RemoveRow(ix)
|
||||||
// Pop off the slice.
|
// Pop off the slice.
|
||||||
c.messageIDs = append(c.messageIDs[:ix], c.messageIDs[ix+1:]...)
|
c.messageList.Remove(elem)
|
||||||
// Delete off the map.
|
// Delete off the map.
|
||||||
delete(c.messages, id)
|
delete(c.messages, id)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteEarliest deletes the n earliest messages. It does nothing if n is or
|
||||||
|
// less than 0.
|
||||||
|
func (c *GridStore) DeleteEarliest(n int) {
|
||||||
|
if n <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since container/list nils out the next element, we can't just call Next
|
||||||
|
// after deleting, so we have to call Next manually before Removing.
|
||||||
|
for elem := c.messageList.Front(); elem != nil && n != 0; n-- {
|
||||||
|
gridMsg := elem.Value.(*gridMessage)
|
||||||
|
delete(c.messages, gridMsg.ID())
|
||||||
|
delete(c.messages, gridMsg.Nonce()) // superfluous delete
|
||||||
|
c.Grid.RemoveRow(0)
|
||||||
|
|
||||||
|
next := elem.Next()
|
||||||
|
c.messageList.Remove(elem)
|
||||||
|
elem = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -393,15 +393,16 @@ func (m *Member) Update(member cchat.ListMember) {
|
||||||
func (m *Member) Popup(evq EventQueuer) {
|
func (m *Member) Popup(evq EventQueuer) {
|
||||||
if len(m.output.Mentions) > 0 {
|
if len(m.output.Mentions) > 0 {
|
||||||
p := labeluri.NewPopoverMentioner(m, m.output.Input, m.output.Mentions[0])
|
p := labeluri.NewPopoverMentioner(m, m.output.Input, m.output.Mentions[0])
|
||||||
p.Ref() // prevent the popover from closing itself
|
if p == nil {
|
||||||
p.SetPosition(gtk.POS_LEFT)
|
return
|
||||||
p.Connect("closed", p.Unref)
|
}
|
||||||
|
|
||||||
// Unbounded concurrency is kind of bad. We should deal with
|
// Unbounded concurrency is kind of bad. We should deal with
|
||||||
// this in the future.
|
// this in the future.
|
||||||
evq.Activate()
|
evq.Activate()
|
||||||
p.Connect("closed", evq.Deactivate)
|
p.Connect("closed", evq.Deactivate)
|
||||||
|
|
||||||
|
p.SetPosition(gtk.POS_LEFT)
|
||||||
p.Popup()
|
p.Popup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
type Container interface {
|
type Container interface {
|
||||||
ID() string
|
ID() string
|
||||||
|
Time() time.Time
|
||||||
AuthorID() string
|
AuthorID() string
|
||||||
AvatarURL() string // avatar
|
AvatarURL() string // avatar
|
||||||
Nonce() string
|
Nonce() string
|
||||||
|
@ -140,6 +141,10 @@ func NewEmptyContainer() *GenericContainer {
|
||||||
Content: ctbox,
|
Content: ctbox,
|
||||||
contentBox: ctbox,
|
contentBox: ctbox,
|
||||||
ContentBody: ctbody,
|
ContentBody: ctbody,
|
||||||
|
|
||||||
|
// Time is important, as it is used to sort messages, so we have to be
|
||||||
|
// careful with this.
|
||||||
|
time: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the custom popup menu to the content label.
|
// Bind the custom popup menu to the content label.
|
||||||
|
|
|
@ -2,7 +2,6 @@ package completion
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
|
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
|
||||||
|
@ -138,20 +137,16 @@ func (c *Completer) onChange() {
|
||||||
t, v, blank := State(c.Buffer)
|
t, v, blank := State(c.Buffer)
|
||||||
c.cursor = v
|
c.cursor = v
|
||||||
|
|
||||||
log.Println("STATE:", t, v, blank)
|
|
||||||
|
|
||||||
// If the cursor is on a blank character, then we should not
|
// If the cursor is on a blank character, then we should not
|
||||||
// autocomplete anything, so we set the states to nil.
|
// autocomplete anything, so we set the states to nil.
|
||||||
if blank {
|
if blank {
|
||||||
c.words = nil
|
c.words = nil
|
||||||
c.index = -1
|
c.index = -1
|
||||||
c.Popdown()
|
c.Popdown()
|
||||||
log.Println("RESET INDEX TO -1")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.words, c.index = c.Splitter(t, v)
|
c.words, c.index = c.Splitter(t, v)
|
||||||
log.Println("INDEX:", c.index)
|
|
||||||
c.complete()
|
c.complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue