1
0
Fork 0
mirror of https://github.com/diamondburned/cchat-gtk.git synced 2024-11-01 03:54:16 +00:00
cchat-gtk/internal/ui/messages/container/container.go

124 lines
3.6 KiB
Go

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"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
"github.com/gotk3/gotk3/gtk"
)
// BacklogLimit is the maximum number of messages to store in the container at
// once.
const BacklogLimit = 35
type GridMessage interface {
message.Container
// Focusable should return a widget that can be focused.
Focusable() gtk.IWidget
// Attach should only be called once.
Attach() []gtk.IWidget
// AttachMenu should override the stored constructor.
AttachMenu(items []menu.Item) // save memory
}
type PresendGridMessage interface {
GridMessage
message.PresendContainer
}
// Container is a generic messages container for children messages for children
// packages.
type Container interface {
gtk.IWidget
// Thread-safe methods.
cchat.MessagesContainer
// Thread-unsafe methods.
CreateMessageUnsafe(cchat.MessageCreate) int
UpdateMessageUnsafe(cchat.MessageUpdate)
DeleteMessageUnsafe(cchat.MessageDelete)
// 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)
// AddPresendMessage adds and displays an unsent message.
AddPresendMessage(msg input.PresendMessage) PresendGridMessage
// LatestMessageFrom returns the last message ID with that author.
LatestMessageFrom(authorID string) (msgID string, ok bool)
// UI methods.
SetFocusHAdjustment(*gtk.Adjustment)
SetFocusVAdjustment(*gtk.Adjustment)
}
// Controller is for menu actions.
type Controller interface {
// BindMenu expects the controller to add actioner into the message.
BindMenu(GridMessage)
// Bottomed returns whether or not the message scroller is at the bottom.
Bottomed() bool
// AuthorEvent is called on message create/update. This is used to update
// the typer state.
OnAuthorEvent(a cchat.Author)
}
// 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
}
const ColumnSpacing = 10
// GridContainer is an implementation of Container, which allows flexible
// message grids.
type GridContainer struct {
*GridStore
Controller
}
var _ Container = (*GridContainer)(nil)
func NewGridContainer(constr Constructor, ctrl Controller) *GridContainer {
return &GridContainer{
GridStore: NewGridStore(constr, ctrl),
Controller: ctrl,
}
}
// 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) int {
// Insert the message first.
ix := c.GridStore.CreateMessageUnsafe(msg)
// Determine if the user is scrolled to the bottom for cleaning up.
if c.Bottomed() {
// Clean up the backlog. The function allows a negative n, which would
// be a no-op.
c.PopEarliestMessages(c.MessagesLen() - BacklogLimit)
}
return ix
}
func (c *GridContainer) CreateMessage(msg cchat.MessageCreate) {
gts.ExecAsync(func() { c.CreateMessageUnsafe(msg) })
}
func (c *GridContainer) UpdateMessage(msg cchat.MessageUpdate) {
gts.ExecAsync(func() { c.UpdateMessageUnsafe(msg) })
}
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
gts.ExecAsync(func() { c.DeleteMessageUnsafe(msg) })
}