2020-06-06 07:44:36 +00:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/message"
|
2021-01-03 04:52:30 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
2020-07-16 05:41:21 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/menu"
|
2021-01-02 09:24:14 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/rich/labeluri"
|
2021-01-03 04:52:30 +00:00
|
|
|
"github.com/diamondburned/handy"
|
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.
|
2021-01-03 09:36:43 +00:00
|
|
|
const BacklogLimit = 50
|
2020-06-13 21:51:03 +00:00
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
type MessageRow interface {
|
2020-06-06 07:44:36 +00:00
|
|
|
message.Container
|
2020-06-13 07:29:32 +00:00
|
|
|
// Attach should only be called once.
|
2021-01-02 09:24:14 +00:00
|
|
|
Row() *gtk.ListBoxRow
|
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
|
2021-01-03 04:52:30 +00:00
|
|
|
// MenuItems returns the list of attached menu items.
|
|
|
|
MenuItems() []menu.Item
|
2021-01-02 09:24:14 +00:00
|
|
|
// SetReferenceHighlighter sets the reference highlighter into the message.
|
|
|
|
SetReferenceHighlighter(refer labeluri.ReferenceHighlighter)
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
type PresendMessageRow interface {
|
|
|
|
MessageRow
|
2020-06-06 07:44:36 +00:00
|
|
|
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
|
|
|
|
2020-11-06 04:01:00 +00:00
|
|
|
// Reset resets the message container to its original state.
|
|
|
|
Reset()
|
|
|
|
|
2020-11-05 19:08:30 +00:00
|
|
|
// CreateMessageUnsafe creates a new message and returns the index that is
|
|
|
|
// the location the message is added to.
|
2020-10-23 06:21:34 +00:00
|
|
|
CreateMessageUnsafe(cchat.MessageCreate)
|
2020-06-13 07:29:32 +00:00
|
|
|
UpdateMessageUnsafe(cchat.MessageUpdate)
|
|
|
|
DeleteMessageUnsafe(cchat.MessageDelete)
|
|
|
|
|
2020-08-20 23:53:13 +00:00
|
|
|
// FirstMessage returns the first message in the buffer. Nil is returned if
|
|
|
|
// there's nothing.
|
2021-01-02 09:24:14 +00:00
|
|
|
FirstMessage() MessageRow
|
2020-06-13 07:29:32 +00:00
|
|
|
// AddPresendMessage adds and displays an unsent message.
|
2021-01-02 09:24:14 +00:00
|
|
|
AddPresendMessage(msg input.PresendMessage) PresendMessageRow
|
2020-06-17 22:58:38 +00:00
|
|
|
// LatestMessageFrom returns the last message ID with that author.
|
|
|
|
LatestMessageFrom(authorID string) (msgID string, ok bool)
|
2021-01-02 09:24:14 +00:00
|
|
|
// Message finds and returns the message, if any.
|
|
|
|
Message(id cchat.ID, nonce string) MessageRow
|
|
|
|
|
2021-01-02 09:55:19 +00:00
|
|
|
// Highlight temporarily highlights the given message for a short while.
|
2021-01-02 09:24:14 +00:00
|
|
|
Highlight(msg MessageRow)
|
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 {
|
2021-01-03 04:52:30 +00:00
|
|
|
// Connector is used for button press events to unselect messages.
|
|
|
|
primitives.Connector
|
2020-06-13 07:29:32 +00:00
|
|
|
// BindMenu expects the controller to add actioner into the message.
|
2021-01-02 09:24:14 +00:00
|
|
|
BindMenu(MessageRow)
|
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-23 06:21:34 +00:00
|
|
|
AuthorEvent(a cchat.Author)
|
2021-01-03 04:52:30 +00:00
|
|
|
// SelectMessage is called when a message is selected.
|
|
|
|
SelectMessage(list *ListStore, msg MessageRow)
|
|
|
|
// UnselectMessage is called when the message selection is cleared.
|
|
|
|
UnselectMessage()
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor is an interface for making custom message implementations which
|
2021-01-02 09:24:14 +00:00
|
|
|
// allows ListContainer to generically work with.
|
2020-06-13 07:29:32 +00:00
|
|
|
type Constructor interface {
|
2021-01-02 09:24:14 +00:00
|
|
|
NewMessage(cchat.MessageCreate) MessageRow
|
|
|
|
NewPresendMessage(input.PresendMessage) PresendMessageRow
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
const ColumnSpacing = 8
|
2020-06-07 04:27:28 +00:00
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
// ListContainer is an implementation of Container, which allows flexible
|
2020-06-06 07:44:36 +00:00
|
|
|
// message grids.
|
2021-01-02 09:24:14 +00:00
|
|
|
type ListContainer struct {
|
2021-01-03 04:52:30 +00:00
|
|
|
*handy.Clamp
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
*ListStore
|
2021-01-03 04:52:30 +00:00
|
|
|
|
2020-07-03 03:22:48 +00:00
|
|
|
Controller
|
2020-06-07 04:27:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
// messageRow w/ required internals
|
|
|
|
type messageRow struct {
|
|
|
|
MessageRow
|
2020-10-23 06:21:34 +00:00
|
|
|
presend message.PresendContainer // this shouldn't be here but i'm lazy
|
|
|
|
}
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
var _ Container = (*ListContainer)(nil)
|
2020-06-06 07:44:36 +00:00
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
func NewListContainer(constr Constructor, ctrl Controller) *ListContainer {
|
2021-01-03 04:52:30 +00:00
|
|
|
listStore := NewListStore(constr, ctrl)
|
|
|
|
listStore.ListBox.Show()
|
|
|
|
|
|
|
|
clamp := handy.ClampNew()
|
|
|
|
clamp.SetMaximumSize(800)
|
|
|
|
clamp.SetTighteningThreshold(600)
|
|
|
|
clamp.SetHExpand(true)
|
|
|
|
clamp.SetVExpand(true)
|
|
|
|
clamp.Add(listStore.ListBox)
|
|
|
|
clamp.Show()
|
|
|
|
|
2021-01-02 09:24:14 +00:00
|
|
|
return &ListContainer{
|
2021-01-03 04:52:30 +00:00
|
|
|
Clamp: clamp,
|
|
|
|
ListStore: listStore,
|
2020-07-03 03:22:48 +00:00
|
|
|
Controller: ctrl,
|
2020-06-06 07:44:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-06 03:23:06 +00:00
|
|
|
// CreateMessageUnsafe inserts a message. It does not clean up old messages.
|
2021-01-02 09:24:14 +00:00
|
|
|
func (c *ListContainer) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
2020-06-13 21:51:03 +00:00
|
|
|
// Insert the message first.
|
2021-01-02 09:24:14 +00:00
|
|
|
c.ListStore.CreateMessageUnsafe(msg)
|
2020-11-06 03:23:06 +00:00
|
|
|
}
|
2020-06-13 21:51:03 +00:00
|
|
|
|
2020-11-06 03:23:06 +00:00
|
|
|
// CleanMessages cleans up the oldest messages if the user is scrolled to the
|
|
|
|
// bottom. True is returned if there were changes.
|
2021-01-02 09:24:14 +00:00
|
|
|
func (c *ListContainer) CleanMessages() bool {
|
2020-06-13 21:51:03 +00:00
|
|
|
// Determine if the user is scrolled to the bottom for cleaning up.
|
2020-10-24 21:58:48 +00:00
|
|
|
if c.Bottomed() {
|
|
|
|
// Clean up the backlog.
|
2020-11-06 03:23:06 +00:00
|
|
|
if delta := c.MessagesLen() - BacklogLimit; delta > 0 {
|
|
|
|
c.DeleteEarliest(delta)
|
|
|
|
return true
|
|
|
|
}
|
2020-10-23 06:21:34 +00:00
|
|
|
}
|
2020-11-06 03:23:06 +00:00
|
|
|
|
|
|
|
return false
|
2020-06-13 21:51:03 +00:00
|
|
|
}
|
2021-01-03 04:52:30 +00:00
|
|
|
|
|
|
|
func (c *ListContainer) SetFocusHAdjustment(adj *gtk.Adjustment) {
|
|
|
|
c.ListBox.SetFocusHAdjustment(adj)
|
|
|
|
}
|
|
|
|
func (c *ListContainer) SetFocusVAdjustment(adj *gtk.Adjustment) {
|
|
|
|
c.ListBox.SetFocusVAdjustment(adj)
|
|
|
|
}
|