mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-01-09 03:56:45 +00:00
Refactored username container; finishing up typing indicator; minor fixes
This commit is contained in:
parent
1caa476a84
commit
68814c2207
|
@ -3,7 +3,6 @@ package container
|
|||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"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/message"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
||||
|
@ -47,7 +46,6 @@ type Container interface {
|
|||
DeleteMessageUnsafe(cchat.MessageDelete)
|
||||
|
||||
Reset()
|
||||
ScrollToBottom()
|
||||
|
||||
// AddPresendMessage adds and displays an unsent message.
|
||||
AddPresendMessage(msg input.PresendMessage) PresendGridMessage
|
||||
|
@ -59,6 +57,10 @@ type Container interface {
|
|||
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
|
||||
// ScrollToBottom scrolls the message view to the bottom.
|
||||
// ScrollToBottom()
|
||||
}
|
||||
|
||||
// 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
|
||||
// message grids.
|
||||
type GridContainer struct {
|
||||
*autoscroll.ScrolledWindow
|
||||
*GridStore
|
||||
Controller
|
||||
}
|
||||
|
||||
// gridMessage w/ required internals
|
||||
|
@ -86,16 +88,9 @@ type gridMessage struct {
|
|||
var _ Container = (*GridContainer)(nil)
|
||||
|
||||
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{
|
||||
ScrolledWindow: sw,
|
||||
GridStore: store,
|
||||
GridStore: NewGridStore(constr, ctrl),
|
||||
Controller: ctrl,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +101,7 @@ func (c *GridContainer) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
|||
c.GridStore.CreateMessageUnsafe(msg)
|
||||
|
||||
// Determine if the user is scrolled to the bottom for cleaning up.
|
||||
if !c.ScrolledWindow.Bottomed {
|
||||
if !c.Bottomed() {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -136,9 +131,3 @@ func (c *GridContainer) UpdateMessage(msg cchat.MessageUpdate) {
|
|||
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
|
||||
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
|
||||
// scrolled to the bottom.
|
||||
if !c.ScrolledWindow.Bottomed {
|
||||
if !c.Bottomed() {
|
||||
// If we're not at the bottom, then we exit.
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
type GridStore struct {
|
||||
Grid *gtk.Grid
|
||||
*gtk.Grid
|
||||
|
||||
Construct Constructor
|
||||
Controller Controller
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"github.com/diamondburned/cchat"
|
||||
"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/username"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/scrollinput"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -20,6 +22,12 @@ type InputView struct {
|
|||
Completer *completion.View
|
||||
}
|
||||
|
||||
var textCSS = primitives.PrepareCSS(`
|
||||
textview, textview * {
|
||||
background-color: transparent;
|
||||
}
|
||||
`)
|
||||
|
||||
func NewView(ctrl Controller) *InputView {
|
||||
text, _ := gtk.TextViewNew()
|
||||
text.SetSensitive(false)
|
||||
|
@ -30,6 +38,9 @@ func NewView(ctrl Controller) *InputView {
|
|||
text.SetProperty("bottom-margin", inputmargin)
|
||||
text.Show()
|
||||
|
||||
primitives.AddClass(text, "message-input")
|
||||
primitives.AttachCSS(text, textCSS)
|
||||
|
||||
// Bind the text event handler to text first.
|
||||
c := completion.New(text)
|
||||
|
||||
|
@ -37,12 +48,7 @@ func NewView(ctrl Controller) *InputView {
|
|||
f := NewField(text, ctrl)
|
||||
f.Show()
|
||||
|
||||
// // 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())
|
||||
// })
|
||||
primitives.AddClass(f, "input-field")
|
||||
|
||||
return &InputView{f, c}
|
||||
}
|
||||
|
@ -57,7 +63,7 @@ func (v *InputView) SetSender(session cchat.Session, sender cchat.ServerMessageS
|
|||
|
||||
type Field struct {
|
||||
*gtk.Box
|
||||
username *usernameContainer
|
||||
Username *username.Container
|
||||
|
||||
TextScroll *gtk.ScrolledWindow
|
||||
text *gtk.TextView
|
||||
|
@ -73,10 +79,11 @@ type Field struct {
|
|||
editingID string // never empty
|
||||
}
|
||||
|
||||
const inputmargin = 4
|
||||
const inputmargin = username.VMargin
|
||||
|
||||
func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
||||
username := newUsernameContainer()
|
||||
username := username.NewContainer()
|
||||
username.SetVAlign(gtk.ALIGN_END)
|
||||
username.Show()
|
||||
|
||||
buf, _ := text.GetBuffer()
|
||||
|
@ -90,8 +97,9 @@ func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
|||
box.Show()
|
||||
|
||||
field := &Field{
|
||||
Box: box,
|
||||
username: username,
|
||||
Box: box,
|
||||
Username: username,
|
||||
// typing: typing,
|
||||
TextScroll: sw,
|
||||
text: text,
|
||||
buffer: buf,
|
||||
|
@ -102,6 +110,13 @@ func NewField(text *gtk.TextView, ctrl Controller) *Field {
|
|||
text.SetFocusVAdjustment(sw.GetVAdjustment())
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -113,7 +128,7 @@ func (f *Field) Reset() {
|
|||
f.UserID = ""
|
||||
f.Sender = nil
|
||||
f.editor = nil
|
||||
f.username.Reset()
|
||||
f.Username.Reset()
|
||||
|
||||
// reset the input
|
||||
f.buffer.Delete(f.buffer.GetBounds())
|
||||
|
@ -123,7 +138,7 @@ func (f *Field) Reset() {
|
|||
// disabled. Reset() should be called first.
|
||||
func (f *Field) SetSender(session cchat.Session, sender cchat.ServerMessageSender) {
|
||||
// Update the left username container in the input.
|
||||
f.username.Update(session, sender)
|
||||
f.Username.Update(session, sender)
|
||||
f.UserID = session.ID()
|
||||
|
||||
// Set the sender.
|
||||
|
|
|
@ -58,9 +58,9 @@ func (f *Field) sendInput() {
|
|||
f.SendMessage(SendMessageData{
|
||||
time: time.Now().UTC(),
|
||||
content: text,
|
||||
author: f.username.GetLabel(),
|
||||
author: f.Username.GetLabel(),
|
||||
authorID: f.UserID,
|
||||
authorURL: f.username.GetIconURL(),
|
||||
authorURL: f.Username.GetIconURL(),
|
||||
nonce: f.generateNonce(),
|
||||
})
|
||||
}
|
||||
|
|
142
internal/ui/messages/input/username/username.go
Normal file
142
internal/ui/messages/input/username/username.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package username
|
||||
|
||||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/config"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
|
||||
"github.com/diamondburned/cchat/text"
|
||||
"github.com/diamondburned/imgutil"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
)
|
||||
|
||||
const AvatarSize = 24
|
||||
const VMargin = 4
|
||||
|
||||
var showUser = true
|
||||
var currentRevealer = func(bool) {} // noop by default
|
||||
|
||||
func init() {
|
||||
// Bind this revealer in settings.
|
||||
config.AppearanceAdd("Show Username in Input", config.Switch(
|
||||
&showUser,
|
||||
func(b bool) { currentRevealer(b) },
|
||||
))
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
*gtk.Revealer
|
||||
main *gtk.Box
|
||||
avatar *rich.Icon
|
||||
label *rich.Label
|
||||
}
|
||||
|
||||
var (
|
||||
_ cchat.LabelContainer = (*Container)(nil)
|
||||
_ cchat.IconContainer = (*Container)(nil)
|
||||
)
|
||||
|
||||
func NewContainer() *Container {
|
||||
avatar := rich.NewIcon(AvatarSize, imgutil.Round(true))
|
||||
avatar.SetPlaceholderIcon("user-available-symbolic", AvatarSize)
|
||||
avatar.Show()
|
||||
|
||||
label := rich.NewLabel(text.Rich{})
|
||||
label.SetMaxWidthChars(35)
|
||||
label.Show()
|
||||
|
||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
|
||||
box.PackStart(avatar, false, false, 0)
|
||||
box.PackStart(label, false, false, 0)
|
||||
box.SetMarginStart(10)
|
||||
box.SetMarginEnd(10)
|
||||
box.SetMarginTop(VMargin)
|
||||
box.SetMarginBottom(VMargin)
|
||||
box.SetVAlign(gtk.ALIGN_START)
|
||||
box.Show()
|
||||
|
||||
rev, _ := gtk.RevealerNew()
|
||||
rev.SetRevealChild(false)
|
||||
rev.SetTransitionType(gtk.REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
||||
rev.SetTransitionDuration(50)
|
||||
rev.Add(box)
|
||||
|
||||
// Bind the current global revealer to this revealer for settings. This
|
||||
// operation should be thread-safe, as everything is being done in the main
|
||||
// thread.
|
||||
currentRevealer = rev.SetRevealChild
|
||||
|
||||
return &Container{
|
||||
Revealer: rev,
|
||||
main: box,
|
||||
avatar: avatar,
|
||||
label: label,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Container) SetRevealChild(reveal bool) {
|
||||
// Only reveal if showUser is true.
|
||||
u.Revealer.SetRevealChild(reveal && showUser)
|
||||
}
|
||||
|
||||
// shouldReveal returns whether or not the container should reveal.
|
||||
func (u *Container) shouldReveal() bool {
|
||||
return !u.label.GetLabel().Empty() && showUser
|
||||
}
|
||||
|
||||
func (u *Container) Reset() {
|
||||
u.SetRevealChild(false)
|
||||
u.avatar.Reset()
|
||||
u.label.Reset()
|
||||
}
|
||||
|
||||
// Update is not thread-safe.
|
||||
func (u *Container) Update(session cchat.Session, sender cchat.ServerMessageSender) {
|
||||
// Set the fallback username.
|
||||
u.label.SetLabelUnsafe(session.Name())
|
||||
// Reveal the name if it's not empty.
|
||||
u.SetRevealChild(u.shouldReveal())
|
||||
|
||||
// Does sender (aka Server) implement ServerNickname? If yes, use it.
|
||||
if nicknamer, ok := sender.(cchat.ServerNickname); ok {
|
||||
u.label.AsyncSetLabel(nicknamer.Nickname, "Error fetching server nickname")
|
||||
}
|
||||
|
||||
// Does session implement an icon? Update if yes.
|
||||
if iconer, ok := session.(cchat.Icon); ok {
|
||||
u.avatar.AsyncSetIconer(iconer, "Error fetching session icon URL")
|
||||
}
|
||||
}
|
||||
|
||||
// GetLabel is not thread-safe.
|
||||
func (u *Container) GetLabel() text.Rich {
|
||||
return u.label.GetLabel()
|
||||
}
|
||||
|
||||
// SetLabel is thread-safe.
|
||||
func (u *Container) SetLabel(content text.Rich) {
|
||||
gts.ExecAsync(func() {
|
||||
u.label.SetLabelUnsafe(content)
|
||||
|
||||
// Reveal if the name is not empty.
|
||||
u.SetRevealChild(u.shouldReveal())
|
||||
})
|
||||
}
|
||||
|
||||
// SetIcon is thread-safe.
|
||||
func (u *Container) SetIcon(url string) {
|
||||
gts.ExecAsync(func() {
|
||||
u.avatar.SetIconUnsafe(url)
|
||||
|
||||
// Reveal if the icon URL is not empty. We don't touch anything if the
|
||||
// URL is empty, as the name might not be.
|
||||
if url != "" {
|
||||
u.SetRevealChild(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetIconURL is not thread-safe.
|
||||
func (u *Container) GetIconURL() string {
|
||||
return u.avatar.URL()
|
||||
}
|
|
@ -81,7 +81,7 @@ func NewEmptyContainer() *GenericContainer {
|
|||
ts.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
|
||||
ts.SetXAlign(1) // right align
|
||||
ts.SetVAlign(gtk.ALIGN_END)
|
||||
ts.SetSelectable(true)
|
||||
// ts.SetSelectable(true)
|
||||
ts.Show()
|
||||
|
||||
user, _ := gtk.LabelNew("")
|
||||
|
@ -90,7 +90,7 @@ func NewEmptyContainer() *GenericContainer {
|
|||
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
||||
user.SetXAlign(1) // right align
|
||||
user.SetVAlign(gtk.ALIGN_START)
|
||||
user.SetSelectable(true)
|
||||
// user.SetSelectable(true)
|
||||
user.Show()
|
||||
|
||||
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/input"
|
||||
"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/gotk3/gotk3/gtk"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -37,7 +39,11 @@ type View struct {
|
|||
*sadface.FaceView
|
||||
Box *gtk.Box
|
||||
|
||||
Scroller *autoscroll.ScrolledWindow
|
||||
InputView *input.InputView
|
||||
|
||||
MsgBox *gtk.Box
|
||||
Typing *typing.Container
|
||||
Container container.Container
|
||||
contType int // msgIndex
|
||||
|
||||
|
@ -47,16 +53,34 @@ type View struct {
|
|||
|
||||
func NewView() *View {
|
||||
view := &View{}
|
||||
view.InputView = input.NewView(view)
|
||||
view.Typing = typing.New()
|
||||
view.Typing.Show()
|
||||
|
||||
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
view.Box.PackEnd(view.InputView, false, false, 0)
|
||||
view.Box.Show()
|
||||
view.MsgBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 2)
|
||||
view.MsgBox.PackEnd(view.Typing, false, false, 0)
|
||||
view.MsgBox.Show()
|
||||
|
||||
// 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.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
|
||||
logo, _ := gtk.ImageNewFromPixbuf(icons.Logo256())
|
||||
logo.Show()
|
||||
|
@ -68,7 +92,7 @@ func NewView() *View {
|
|||
func (v *View) createMessageContainer() {
|
||||
// Remove the old message container.
|
||||
if v.Container != nil {
|
||||
v.Box.Remove(v.Container)
|
||||
v.MsgBox.Remove(v.Container)
|
||||
}
|
||||
|
||||
// Update the container type.
|
||||
|
@ -80,15 +104,20 @@ func (v *View) createMessageContainer() {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
v.state.Reset() // Reset the state variables.
|
||||
v.FaceView.Reset() // Switch back to the main screen.
|
||||
v.InputView.Reset() // Reset the input.
|
||||
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.
|
||||
if v.contType != msgIndex {
|
||||
v.createMessageContainer()
|
||||
|
|
|
@ -48,7 +48,7 @@ func NewCompleter(input *gtk.TextView, ctrl Completeable) *Completer {
|
|||
input.Connect("key-press-event", KeyDownHandler(l, input.GrabFocus))
|
||||
|
||||
ibuf, _ := input.GetBuffer()
|
||||
ibuf.Connect("changed", func() {
|
||||
ibuf.Connect("end-user-action", func() {
|
||||
t, v := State(ibuf)
|
||||
c.Cursor = v
|
||||
c.Words, c.Index = split.SpaceIndexed(t, v)
|
||||
|
|
Loading…
Reference in a new issue