mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-11-26 06:07:26 +00:00
WIP, rebase me
This commit is contained in:
parent
0200da67e0
commit
8c5ecd418e
|
|
@ -3,7 +3,6 @@ package container
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/autoscroll"
|
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
"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/messages/message"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
||||||
|
|
@ -47,7 +46,6 @@ type Container interface {
|
||||||
DeleteMessageUnsafe(cchat.MessageDelete)
|
DeleteMessageUnsafe(cchat.MessageDelete)
|
||||||
|
|
||||||
Reset()
|
Reset()
|
||||||
ScrollToBottom()
|
|
||||||
|
|
||||||
// AddPresendMessage adds and displays an unsent message.
|
// AddPresendMessage adds and displays an unsent message.
|
||||||
AddPresendMessage(msg input.PresendMessage) PresendGridMessage
|
AddPresendMessage(msg input.PresendMessage) PresendGridMessage
|
||||||
|
|
@ -59,6 +57,10 @@ type Container interface {
|
||||||
type Controller interface {
|
type Controller interface {
|
||||||
// BindMenu expects the controller to add actioner into the message.
|
// BindMenu expects the controller to add actioner into the message.
|
||||||
BindMenu(GridMessage)
|
BindMenu(GridMessage)
|
||||||
|
// Bottomed returns whether or not the message scroller is at the bottom.
|
||||||
|
Bottomed() bool
|
||||||
|
// ScrollToBottom scrolls the message view to the bottom.
|
||||||
|
// ScrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor is an interface for making custom message implementations which
|
// Constructor is an interface for making custom message implementations which
|
||||||
|
|
@ -73,8 +75,8 @@ const ColumnSpacing = 10
|
||||||
// GridContainer is an implementation of Container, which allows flexible
|
// GridContainer is an implementation of Container, which allows flexible
|
||||||
// message grids.
|
// message grids.
|
||||||
type GridContainer struct {
|
type GridContainer struct {
|
||||||
*autoscroll.ScrolledWindow
|
|
||||||
*GridStore
|
*GridStore
|
||||||
|
Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// gridMessage w/ required internals
|
// gridMessage w/ required internals
|
||||||
|
|
@ -86,16 +88,9 @@ type gridMessage struct {
|
||||||
var _ Container = (*GridContainer)(nil)
|
var _ Container = (*GridContainer)(nil)
|
||||||
|
|
||||||
func NewGridContainer(constr Constructor, ctrl Controller) *GridContainer {
|
func NewGridContainer(constr Constructor, ctrl Controller) *GridContainer {
|
||||||
store := NewGridStore(constr, ctrl)
|
|
||||||
|
|
||||||
sw := autoscroll.NewScrolledWindow()
|
|
||||||
sw.Add(store.Grid)
|
|
||||||
sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
||||||
sw.Show()
|
|
||||||
|
|
||||||
return &GridContainer{
|
return &GridContainer{
|
||||||
ScrolledWindow: sw,
|
GridStore: NewGridStore(constr, ctrl),
|
||||||
GridStore: store,
|
Controller: ctrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +101,7 @@ 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.ScrolledWindow.Bottomed {
|
if !c.Bottomed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,9 +131,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) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset is not thread-safe.
|
|
||||||
func (c *GridContainer) Reset() {
|
|
||||||
c.GridStore.Reset()
|
|
||||||
c.ScrolledWindow.Bottomed = true
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||||
|
|
||||||
// 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.ScrolledWindow.Bottomed {
|
if !c.Bottomed() {
|
||||||
// If we're not at the bottom, then we exit.
|
// If we're not at the bottom, then we exit.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type GridStore struct {
|
type GridStore struct {
|
||||||
Grid *gtk.Grid
|
*gtk.Grid
|
||||||
|
|
||||||
Construct Constructor
|
Construct Constructor
|
||||||
Controller Controller
|
Controller Controller
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input/completion"
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input/completion"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input/username"
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input/username"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/scrollinput"
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/scrollinput"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
@ -21,6 +22,12 @@ type InputView struct {
|
||||||
Completer *completion.View
|
Completer *completion.View
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var textCSS = primitives.PrepareCSS(`
|
||||||
|
textview, textview * {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
func NewView(ctrl Controller) *InputView {
|
func NewView(ctrl Controller) *InputView {
|
||||||
text, _ := gtk.TextViewNew()
|
text, _ := gtk.TextViewNew()
|
||||||
text.SetSensitive(false)
|
text.SetSensitive(false)
|
||||||
|
|
@ -31,6 +38,9 @@ func NewView(ctrl Controller) *InputView {
|
||||||
text.SetProperty("bottom-margin", inputmargin)
|
text.SetProperty("bottom-margin", inputmargin)
|
||||||
text.Show()
|
text.Show()
|
||||||
|
|
||||||
|
primitives.AddClass(text, "message-input")
|
||||||
|
primitives.AttachCSS(text, textCSS)
|
||||||
|
|
||||||
// Bind the text event handler to text first.
|
// Bind the text event handler to text first.
|
||||||
c := completion.New(text)
|
c := completion.New(text)
|
||||||
|
|
||||||
|
|
@ -38,12 +48,7 @@ func NewView(ctrl Controller) *InputView {
|
||||||
f := NewField(text, ctrl)
|
f := NewField(text, ctrl)
|
||||||
f.Show()
|
f.Show()
|
||||||
|
|
||||||
// // Connect to the field's revealer. On resize, we want the autocompleter to
|
primitives.AddClass(f, "input-field")
|
||||||
// // have the right padding too.
|
|
||||||
// f.username.Connect("size-allocate", func(w gtk.IWidget) {
|
|
||||||
// // Set the autocompleter's left margin to be the same.
|
|
||||||
// c.SetMarginStart(w.ToWidget().GetAllocatedWidth())
|
|
||||||
// })
|
|
||||||
|
|
||||||
return &InputView{f, c}
|
return &InputView{f, c}
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +63,7 @@ func (v *InputView) SetSender(session cchat.Session, sender cchat.ServerMessageS
|
||||||
|
|
||||||
type Field struct {
|
type Field struct {
|
||||||
*gtk.Box
|
*gtk.Box
|
||||||
username *username.Container
|
Username *username.Container
|
||||||
|
|
||||||
TextScroll *gtk.ScrolledWindow
|
TextScroll *gtk.ScrolledWindow
|
||||||
text *gtk.TextView
|
text *gtk.TextView
|
||||||
|
|
@ -78,6 +83,7 @@ const inputmargin = username.VMargin
|
||||||
|
|
||||||
func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
||||||
username := username.NewContainer()
|
username := username.NewContainer()
|
||||||
|
username.SetVAlign(gtk.ALIGN_END)
|
||||||
username.Show()
|
username.Show()
|
||||||
|
|
||||||
buf, _ := text.GetBuffer()
|
buf, _ := text.GetBuffer()
|
||||||
|
|
@ -91,8 +97,9 @@ func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
||||||
box.Show()
|
box.Show()
|
||||||
|
|
||||||
field := &Field{
|
field := &Field{
|
||||||
Box: box,
|
Box: box,
|
||||||
username: username,
|
Username: username,
|
||||||
|
// typing: typing,
|
||||||
TextScroll: sw,
|
TextScroll: sw,
|
||||||
text: text,
|
text: text,
|
||||||
buffer: buf,
|
buffer: buf,
|
||||||
|
|
@ -103,6 +110,13 @@ func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
||||||
text.SetFocusVAdjustment(sw.GetVAdjustment())
|
text.SetFocusVAdjustment(sw.GetVAdjustment())
|
||||||
text.Connect("key-press-event", field.keyDown)
|
text.Connect("key-press-event", field.keyDown)
|
||||||
|
|
||||||
|
// // Connect to the field's revealer. On resize, we want the autocompleter to
|
||||||
|
// // have the right padding too.
|
||||||
|
// f.username.Connect("size-allocate", func(w gtk.IWidget) {
|
||||||
|
// // Set the autocompleter's left margin to be the same.
|
||||||
|
// c.SetMarginStart(w.ToWidget().GetAllocatedWidth())
|
||||||
|
// })
|
||||||
|
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +128,7 @@ func (f *Field) Reset() {
|
||||||
f.UserID = ""
|
f.UserID = ""
|
||||||
f.Sender = nil
|
f.Sender = nil
|
||||||
f.editor = nil
|
f.editor = nil
|
||||||
f.username.Reset()
|
f.Username.Reset()
|
||||||
|
|
||||||
// reset the input
|
// reset the input
|
||||||
f.buffer.Delete(f.buffer.GetBounds())
|
f.buffer.Delete(f.buffer.GetBounds())
|
||||||
|
|
@ -124,7 +138,7 @@ func (f *Field) Reset() {
|
||||||
// disabled. Reset() should be called first.
|
// disabled. Reset() should be called first.
|
||||||
func (f *Field) SetSender(session cchat.Session, sender cchat.ServerMessageSender) {
|
func (f *Field) SetSender(session cchat.Session, sender cchat.ServerMessageSender) {
|
||||||
// Update the left username container in the input.
|
// Update the left username container in the input.
|
||||||
f.username.Update(session, sender)
|
f.Username.Update(session, sender)
|
||||||
f.UserID = session.ID()
|
f.UserID = session.ID()
|
||||||
|
|
||||||
// Set the sender.
|
// Set the sender.
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,9 @@ func (f *Field) sendInput() {
|
||||||
f.SendMessage(SendMessageData{
|
f.SendMessage(SendMessageData{
|
||||||
time: time.Now().UTC(),
|
time: time.Now().UTC(),
|
||||||
content: text,
|
content: text,
|
||||||
author: f.username.GetLabel(),
|
author: f.Username.GetLabel(),
|
||||||
authorID: f.UserID,
|
authorID: f.UserID,
|
||||||
authorURL: f.username.GetIconURL(),
|
authorURL: f.Username.GetIconURL(),
|
||||||
nonce: f.generateNonce(),
|
nonce: f.generateNonce(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ func NewEmptyContainer() *GenericContainer {
|
||||||
ts.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
|
ts.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
|
||||||
ts.SetXAlign(1) // right align
|
ts.SetXAlign(1) // right align
|
||||||
ts.SetVAlign(gtk.ALIGN_END)
|
ts.SetVAlign(gtk.ALIGN_END)
|
||||||
ts.SetSelectable(true)
|
// ts.SetSelectable(true)
|
||||||
ts.Show()
|
ts.Show()
|
||||||
|
|
||||||
user, _ := gtk.LabelNew("")
|
user, _ := gtk.LabelNew("")
|
||||||
|
|
@ -90,7 +90,7 @@ func NewEmptyContainer() *GenericContainer {
|
||||||
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
||||||
user.SetXAlign(1) // right align
|
user.SetXAlign(1) // right align
|
||||||
user.SetVAlign(gtk.ALIGN_START)
|
user.SetVAlign(gtk.ALIGN_START)
|
||||||
user.SetSelectable(true)
|
// user.SetSelectable(true)
|
||||||
user.Show()
|
user.Show()
|
||||||
|
|
||||||
content, _ := gtk.LabelNew("")
|
content, _ := gtk.LabelNew("")
|
||||||
|
|
|
||||||
56
internal/ui/messages/typing/dots.go
Normal file
56
internal/ui/messages/typing/dots.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package typing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dotsCSS = primitives.PrepareCSS(`
|
||||||
|
@keyframes breathing {
|
||||||
|
0% { opacity: 0.66; }
|
||||||
|
100% { opacity: 0.12; }
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
animation: breathing 800ms infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:nth-child(1) {
|
||||||
|
animation-delay: 000ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:nth-child(2) {
|
||||||
|
animation-delay: 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:nth-child(3) {
|
||||||
|
animation-delay: 300ms;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
const breathingChar = "●"
|
||||||
|
|
||||||
|
func NewDots() *gtk.Box {
|
||||||
|
c1, _ := gtk.LabelNew(breathingChar)
|
||||||
|
c1.Show()
|
||||||
|
c2, _ := gtk.LabelNew(breathingChar)
|
||||||
|
c2.Show()
|
||||||
|
c3, _ := gtk.LabelNew(breathingChar)
|
||||||
|
c3.Show()
|
||||||
|
|
||||||
|
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
|
b.Add(c1)
|
||||||
|
b.Add(c2)
|
||||||
|
b.Add(c3)
|
||||||
|
|
||||||
|
primitives.AddClass(b, "breathing-dots")
|
||||||
|
|
||||||
|
primitives.AttachCSS(c1, dotsCSS)
|
||||||
|
primitives.AttachCSS(c1, smallfonts)
|
||||||
|
primitives.AttachCSS(c2, dotsCSS)
|
||||||
|
primitives.AttachCSS(c2, smallfonts)
|
||||||
|
primitives.AttachCSS(c3, dotsCSS)
|
||||||
|
primitives.AttachCSS(c3, smallfonts)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
76
internal/ui/messages/typing/typing.go
Normal file
76
internal/ui/messages/typing/typing.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
package typing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/diamondburned/cchat"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input/username"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
"github.com/gotk3/gotk3/pango"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
typers []cchat.Typer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewState() *State {
|
||||||
|
return &State{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) Empty() bool {
|
||||||
|
// return len(s.typers) == 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var typingIndicatorCSS = primitives.PrepareCSS(`
|
||||||
|
.typing-indicator {
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
color: alpha(@theme_fg_color, 0.8);
|
||||||
|
background-color: @theme_base_color;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var smallfonts = primitives.PrepareCSS(`
|
||||||
|
* { font-size: 0.9em; }
|
||||||
|
`)
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
*gtk.Revealer
|
||||||
|
empty bool // && state.Empty()
|
||||||
|
State *State
|
||||||
|
}
|
||||||
|
|
||||||
|
const placeholder = "Bruh moment..."
|
||||||
|
|
||||||
|
func New() *Container {
|
||||||
|
d := NewDots()
|
||||||
|
d.Show()
|
||||||
|
|
||||||
|
l, _ := gtk.LabelNew(placeholder)
|
||||||
|
l.SetXAlign(0)
|
||||||
|
l.SetEllipsize(pango.ELLIPSIZE_END)
|
||||||
|
l.Show()
|
||||||
|
primitives.AttachCSS(l, smallfonts)
|
||||||
|
|
||||||
|
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
|
b.PackStart(d, false, false, username.VMargin)
|
||||||
|
b.PackStart(l, true, true, 0)
|
||||||
|
b.SetMarginStart(username.VMargin * 2)
|
||||||
|
b.SetMarginEnd(username.VMargin * 2)
|
||||||
|
b.Show()
|
||||||
|
|
||||||
|
r, _ := gtk.RevealerNew()
|
||||||
|
r.SetTransitionDuration(50)
|
||||||
|
r.SetTransitionType(gtk.REVEALER_TRANSITION_TYPE_CROSSFADE)
|
||||||
|
r.SetRevealChild(true)
|
||||||
|
r.Add(b)
|
||||||
|
|
||||||
|
state := NewState()
|
||||||
|
|
||||||
|
primitives.AddClass(b, "typing-indicator")
|
||||||
|
primitives.AttachCSS(b, typingIndicatorCSS)
|
||||||
|
|
||||||
|
return &Container{
|
||||||
|
Revealer: r,
|
||||||
|
State: state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container/cozy"
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container/cozy"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/sadface"
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/sadface"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/messages/typing"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/autoscroll"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
@ -37,7 +39,11 @@ type View struct {
|
||||||
*sadface.FaceView
|
*sadface.FaceView
|
||||||
Box *gtk.Box
|
Box *gtk.Box
|
||||||
|
|
||||||
|
Scroller *autoscroll.ScrolledWindow
|
||||||
InputView *input.InputView
|
InputView *input.InputView
|
||||||
|
|
||||||
|
MsgBox *gtk.Box
|
||||||
|
Typing *typing.Container
|
||||||
Container container.Container
|
Container container.Container
|
||||||
contType int // msgIndex
|
contType int // msgIndex
|
||||||
|
|
||||||
|
|
@ -47,16 +53,34 @@ type View struct {
|
||||||
|
|
||||||
func NewView() *View {
|
func NewView() *View {
|
||||||
view := &View{}
|
view := &View{}
|
||||||
view.InputView = input.NewView(view)
|
view.Typing = typing.New()
|
||||||
|
view.Typing.Show()
|
||||||
|
|
||||||
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
view.MsgBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 2)
|
||||||
view.Box.PackEnd(view.InputView, false, false, 0)
|
view.MsgBox.PackEnd(view.Typing, false, false, 0)
|
||||||
view.Box.Show()
|
view.MsgBox.Show()
|
||||||
|
|
||||||
// Create the message container, which will use PackEnd to add the widget on
|
// Create the message container, which will use PackEnd to add the widget on
|
||||||
// TOP of the input view.
|
// TOP of the typing indicator.
|
||||||
view.createMessageContainer()
|
view.createMessageContainer()
|
||||||
|
|
||||||
|
view.Scroller = autoscroll.NewScrolledWindow()
|
||||||
|
view.Scroller.Add(view.MsgBox)
|
||||||
|
view.Scroller.Show()
|
||||||
|
|
||||||
|
// A separator to go inbetween.
|
||||||
|
sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_HORIZONTAL)
|
||||||
|
sep.Show()
|
||||||
|
|
||||||
|
view.InputView = input.NewView(view)
|
||||||
|
view.InputView.Show()
|
||||||
|
|
||||||
|
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
|
view.Box.PackStart(view.Scroller, true, true, 0)
|
||||||
|
view.Box.PackStart(sep, false, false, 0)
|
||||||
|
view.Box.PackStart(view.InputView, false, false, 0)
|
||||||
|
view.Box.Show()
|
||||||
|
|
||||||
// placeholder logo
|
// placeholder logo
|
||||||
logo, _ := gtk.ImageNewFromPixbuf(icons.Logo256())
|
logo, _ := gtk.ImageNewFromPixbuf(icons.Logo256())
|
||||||
logo.Show()
|
logo.Show()
|
||||||
|
|
@ -68,7 +92,7 @@ func NewView() *View {
|
||||||
func (v *View) createMessageContainer() {
|
func (v *View) createMessageContainer() {
|
||||||
// Remove the old message container.
|
// Remove the old message container.
|
||||||
if v.Container != nil {
|
if v.Container != nil {
|
||||||
v.Box.Remove(v.Container)
|
v.MsgBox.Remove(v.Container)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the container type.
|
// Update the container type.
|
||||||
|
|
@ -80,15 +104,20 @@ func (v *View) createMessageContainer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new message container.
|
// Add the new message container.
|
||||||
v.Box.PackEnd(v.Container, true, true, 0)
|
v.MsgBox.PackEnd(v.Container, true, true, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *View) Bottomed() bool { return v.Scroller.Bottomed }
|
||||||
|
|
||||||
func (v *View) Reset() {
|
func (v *View) Reset() {
|
||||||
v.state.Reset() // Reset the state variables.
|
v.state.Reset() // Reset the state variables.
|
||||||
v.FaceView.Reset() // Switch back to the main screen.
|
v.FaceView.Reset() // Switch back to the main screen.
|
||||||
v.InputView.Reset() // Reset the input.
|
v.InputView.Reset() // Reset the input.
|
||||||
v.Container.Reset() // Clean all messages.
|
v.Container.Reset() // Clean all messages.
|
||||||
|
|
||||||
|
// Keep the scroller at the bottom.
|
||||||
|
v.Scroller.Bottomed = true
|
||||||
|
|
||||||
// Recreate the message container if the type is different.
|
// Recreate the message container if the type is different.
|
||||||
if v.contType != msgIndex {
|
if v.contType != msgIndex {
|
||||||
v.createMessageContainer()
|
v.createMessageContainer()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func NewCompleter(input *gtk.TextView, ctrl Completeable) *Completer {
|
||||||
input.Connect("key-press-event", KeyDownHandler(l, input.GrabFocus))
|
input.Connect("key-press-event", KeyDownHandler(l, input.GrabFocus))
|
||||||
|
|
||||||
ibuf, _ := input.GetBuffer()
|
ibuf, _ := input.GetBuffer()
|
||||||
ibuf.Connect("changed", func() {
|
ibuf.Connect("end-user-action", func() {
|
||||||
t, v := State(ibuf)
|
t, v := State(ibuf)
|
||||||
c.Cursor = v
|
c.Cursor = v
|
||||||
c.Words, c.Index = split.SpaceIndexed(t, v)
|
c.Words, c.Index = split.SpaceIndexed(t, v)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue