2020-06-06 07:44:36 +00:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/message"
|
2020-07-16 05:41:21 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
|
2020-06-06 07:44:36 +00:00
|
|
|
"github.com/gotk3/gotk3/gtk"
|
|
|
|
)
|
|
|
|
|
2020-06-13 21:51:03 +00:00
|
|
|
// BacklogLimit is the maximum number of messages to store in the container at
|
|
|
|
// once.
|
|
|
|
const BacklogLimit = 35
|
|
|
|
|
2020-06-06 07:44:36 +00:00
|
|
|
type GridMessage interface {
|
|
|
|
message.Container
|
2020-08-20 23:53:13 +00:00
|
|
|
// Focusable should return a widget that can be focused.
|
|
|
|
Focusable() gtk.IWidget
|
2020-06-13 07:29:32 +00:00
|
|
|
// Attach should only be called once.
|
2020-06-06 07:44:36 +00:00
|
|
|
Attach(grid *gtk.Grid, row int)
|
2020-06-13 07:29:32 +00:00
|
|
|
// AttachMenu should override the stored constructor.
|
2020-06-17 22:58:38 +00:00
|
|
|
AttachMenu(items []menu.Item) // save memory
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func AttachRow(grid *gtk.Grid, row int, widgets ...gtk.IWidget) {
|
|
|
|
for i, w := range widgets {
|
|
|
|
grid.Attach(w, i, row, 1, 1)
|
|
|
|
}
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type PresendGridMessage interface {
|
|
|
|
GridMessage
|
|
|
|
message.PresendContainer
|
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Container is a generic messages container for children messages for children
|
|
|
|
// packages.
|
2020-06-06 07:44:36 +00:00
|
|
|
type Container interface {
|
|
|
|
gtk.IWidget
|
2020-06-13 07:29:32 +00:00
|
|
|
|
|
|
|
// Thread-safe methods.
|
2020-06-06 07:44:36 +00:00
|
|
|
cchat.MessagesContainer
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Thread-unsafe methods.
|
|
|
|
CreateMessageUnsafe(cchat.MessageCreate)
|
|
|
|
UpdateMessageUnsafe(cchat.MessageUpdate)
|
|
|
|
DeleteMessageUnsafe(cchat.MessageDelete)
|
2020-08-20 23:53:13 +00:00
|
|
|
PrependMessageUnsafe(cchat.MessageCreate)
|
2020-06-13 07:29:32 +00:00
|
|
|
|
2020-08-20 23:53:13 +00:00
|
|
|
// FirstMessage returns the first message in the buffer. Nil is returned if
|
|
|
|
// there's nothing.
|
|
|
|
FirstMessage() GridMessage
|
|
|
|
// TranslateCoordinates is used for scrolling to the message.
|
|
|
|
TranslateCoordinates(parent gtk.IWidget, msg GridMessage) (y int)
|
2020-06-13 07:29:32 +00:00
|
|
|
// AddPresendMessage adds and displays an unsent message.
|
|
|
|
AddPresendMessage(msg input.PresendMessage) PresendGridMessage
|
2020-06-17 22:58:38 +00:00
|
|
|
// LatestMessageFrom returns the last message ID with that author.
|
|
|
|
LatestMessageFrom(authorID string) (msgID string, ok bool)
|
2020-08-20 23:53:13 +00:00
|
|
|
|
|
|
|
// UI methods.
|
|
|
|
|
|
|
|
SetFocusHAdjustment(*gtk.Adjustment)
|
|
|
|
SetFocusVAdjustment(*gtk.Adjustment)
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
// Controller is for menu actions.
|
|
|
|
type Controller interface {
|
|
|
|
// BindMenu expects the controller to add actioner into the message.
|
|
|
|
BindMenu(GridMessage)
|
2020-07-03 03:22:48 +00:00
|
|
|
// Bottomed returns whether or not the message scroller is at the bottom.
|
|
|
|
Bottomed() bool
|
2020-07-04 04:41:12 +00:00
|
|
|
// AuthorEvent is called on message create/update. This is used to update
|
|
|
|
// the typer state.
|
2020-10-15 06:32:11 +00:00
|
|
|
AuthorEvent(a cchat.Author)
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor is an interface for making custom message implementations which
|
|
|
|
// allows GridContainer to generically work with.
|
|
|
|
type Constructor interface {
|
|
|
|
NewMessage(cchat.MessageCreate) GridMessage
|
|
|
|
NewPresendMessage(input.PresendMessage) PresendGridMessage
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const ColumnSpacing = 10
|
|
|
|
|
2020-06-06 07:44:36 +00:00
|
|
|
// GridContainer is an implementation of Container, which allows flexible
|
|
|
|
// message grids.
|
|
|
|
type GridContainer struct {
|
2020-06-13 07:29:32 +00:00
|
|
|
*GridStore
|
2020-07-03 03:22:48 +00:00
|
|
|
Controller
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// gridMessage w/ required internals
|
|
|
|
type gridMessage struct {
|
|
|
|
GridMessage
|
|
|
|
presend message.PresendContainer // this shouldn't be here but i'm lazy
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
var _ Container = (*GridContainer)(nil)
|
2020-06-06 07:44:36 +00:00
|
|
|
|
2020-06-13 07:29:32 +00:00
|
|
|
func NewGridContainer(constr Constructor, ctrl Controller) *GridContainer {
|
|
|
|
return &GridContainer{
|
2020-07-03 03:22:48 +00:00
|
|
|
GridStore: NewGridStore(constr, ctrl),
|
|
|
|
Controller: ctrl,
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 21:51:03 +00:00
|
|
|
// CreateMessageUnsafe inserts a message as well as cleaning up the backlog if
|
|
|
|
// the user is scrolled to the bottom.
|
|
|
|
func (c *GridContainer) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
|
|
|
// Insert the message first.
|
|
|
|
c.GridStore.CreateMessageUnsafe(msg)
|
|
|
|
|
|
|
|
// Determine if the user is scrolled to the bottom for cleaning up.
|
2020-07-03 03:22:48 +00:00
|
|
|
if !c.Bottomed() {
|
2020-06-13 21:51:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-06 07:44:36 +00:00
|
|
|
func (c *GridContainer) CreateMessage(msg cchat.MessageCreate) {
|
2020-06-13 07:29:32 +00:00
|
|
|
gts.ExecAsync(func() { c.CreateMessageUnsafe(msg) })
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GridContainer) UpdateMessage(msg cchat.MessageUpdate) {
|
2020-06-13 07:29:32 +00:00
|
|
|
gts.ExecAsync(func() { c.UpdateMessageUnsafe(msg) })
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
|
2020-06-13 07:29:32 +00:00
|
|
|
gts.ExecAsync(func() { c.DeleteMessageUnsafe(msg) })
|
|
|
|
}
|
2020-08-20 23:53:13 +00:00
|
|
|
|
|
|
|
func (c *GridContainer) PrependMessage(msg cchat.MessageCreate) {
|
|
|
|
gts.ExecAsync(func() { c.PrependMessageUnsafe(msg) })
|
|
|
|
}
|