mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-11-18 12:12:45 +00:00
Added partial support for the Commander API
This commit is contained in:
parent
0a353ff128
commit
47e3e67b95
3
go.mod
3
go.mod
|
@ -2,7 +2,7 @@ module github.com/diamondburned/cchat-gtk
|
|||
|
||||
go 1.14
|
||||
|
||||
replace github.com/gotk3/gotk3 => github.com/diamondburned/gotk3 v0.0.0-20200619213419-0533bcce0dd6
|
||||
replace github.com/gotk3/gotk3 => github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d
|
||||
|
||||
require (
|
||||
github.com/Xuanwo/go-locale v0.2.0
|
||||
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/diamondburned/imgutil v0.0.0-20200611215339-650ac7cfaf64
|
||||
github.com/goodsign/monday v1.0.0
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
||||
github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279
|
||||
|
|
4
go.sum
4
go.sum
|
@ -69,6 +69,8 @@ github.com/diamondburned/cchat-mock v0.0.0-20200630025821-605d61d89288 h1:ApNV7D
|
|||
github.com/diamondburned/cchat-mock v0.0.0-20200630025821-605d61d89288/go.mod h1:Tu+8b1iz9NGeQb2jmndXn+dQ9zBUa8a8ktK9hL5aaxw=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20200619213419-0533bcce0dd6 h1:ZzLrfQqszhzWI7zqwltzQIWtppfcL7m2aIEpB4kuqx0=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20200619213419-0533bcce0dd6/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d h1:Ha/I6PMKi+B4hpWclwlXj0tUMehR7Q0TNxPczzBwzPI=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
||||
github.com/diamondburned/imgutil v0.0.0-20200611215339-650ac7cfaf64 h1:/ykUYHuYyj+NN/aaqe6lfaCZQc3EMZs93wAGVJTh5j0=
|
||||
github.com/diamondburned/imgutil v0.0.0-20200611215339-650ac7cfaf64/go.mod h1:kBQKaukR/LyCfhED99/T4/XxUMDNEEzf1Fx6vreD3RQ=
|
||||
github.com/diamondburned/ningen v0.1.0 h1:cTnRNrN0g2Wr/kgjLLpa3pqlbEd6JPNa1yGDer8uV4U=
|
||||
|
@ -119,6 +121,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
|
|||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
|
|
|
@ -3,7 +3,7 @@ package container
|
|||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/autoscroll"
|
||||
"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"
|
||||
|
|
57
internal/ui/primitives/buttonoverlay/buttonoverlay.go
Normal file
57
internal/ui/primitives/buttonoverlay/buttonoverlay.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package buttonoverlay
|
||||
|
||||
import "github.com/gotk3/gotk3/gtk"
|
||||
|
||||
type Widget interface {
|
||||
gtk.IWidget
|
||||
SetMarginEnd(int)
|
||||
SetSizeRequest(int, int)
|
||||
SetHAlign(gtk.Align)
|
||||
}
|
||||
|
||||
var _ Widget = (*gtk.Widget)(nil)
|
||||
|
||||
type Button interface {
|
||||
Widget
|
||||
// Bin
|
||||
GetChild() (gtk.IWidget, error)
|
||||
// Container
|
||||
Add(gtk.IWidget)
|
||||
Remove(gtk.IWidget)
|
||||
// Button
|
||||
SetRelief(gtk.ReliefStyle)
|
||||
}
|
||||
|
||||
var _ Button = (*gtk.Button)(nil)
|
||||
|
||||
// Wrap wraps maincontent inside an overlay with smallbutton placed rightmost on
|
||||
// top of the content. It will also set the margins and aligns widgets.
|
||||
func Wrap(maincontent Widget, smallbutton Button, size int) *gtk.Overlay {
|
||||
maincontent.SetMarginEnd(size)
|
||||
smallbutton.SetSizeRequest(size, size)
|
||||
smallbutton.SetHAlign(gtk.ALIGN_END)
|
||||
smallbutton.SetRelief(gtk.RELIEF_NONE)
|
||||
|
||||
o, _ := gtk.OverlayNew()
|
||||
o.Add(maincontent)
|
||||
o.AddOverlay(smallbutton)
|
||||
o.Show()
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Take takes over the given button and replaces its content with the wrapped
|
||||
// overlay, which has the old content as well as the smaller button on top.
|
||||
func Take(b, smallbutton Button, size int) {
|
||||
childv, _ := b.GetChild()
|
||||
widget := childv.ToWidget()
|
||||
|
||||
// As GetChild doesn't reference, we'll want our own reference.
|
||||
widget.Ref()
|
||||
defer widget.Unref()
|
||||
|
||||
// This will unreference.
|
||||
b.Remove(widget)
|
||||
// Wrap will reference.
|
||||
b.Add(Wrap(widget, smallbutton, size))
|
||||
}
|
|
@ -2,6 +2,7 @@ package service
|
|||
|
||||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/buttonoverlay"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/config"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
||||
|
@ -12,62 +13,35 @@ import (
|
|||
const IconSize = 32
|
||||
|
||||
type header struct {
|
||||
*gtk.ToggleButton // no rich text here but it's left aligned
|
||||
|
||||
box *gtk.Box
|
||||
label *rich.Label
|
||||
icon *rich.Icon
|
||||
Add *gtk.Button
|
||||
*rich.ToggleButtonImage
|
||||
Add *gtk.Button
|
||||
|
||||
Menu *menu.LazyMenu
|
||||
}
|
||||
|
||||
func newHeader(svc cchat.Service) *header {
|
||||
i := rich.NewIcon(0)
|
||||
i.AddProcessors(imgutil.Round(true))
|
||||
i.SetPlaceholderIcon("folder-remote-symbolic", IconSize)
|
||||
i.Show()
|
||||
b := rich.NewToggleButtonImage(svc.Name())
|
||||
b.Image.AddProcessors(imgutil.Round(true))
|
||||
b.Image.SetPlaceholderIcon("folder-remote-symbolic", IconSize)
|
||||
b.SetRelief(gtk.RELIEF_NONE)
|
||||
b.SetMode(true)
|
||||
b.Show()
|
||||
|
||||
if iconer, ok := svc.(cchat.Icon); ok {
|
||||
i.AsyncSetIconer(iconer, "Error getting session logo")
|
||||
b.Image.AsyncSetIconer(iconer, "Error getting session logo")
|
||||
}
|
||||
|
||||
l := rich.NewLabel(svc.Name())
|
||||
l.Show()
|
||||
|
||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
box.PackStart(i, false, false, 0)
|
||||
box.PackStart(l, true, true, 5)
|
||||
box.SetMarginEnd(IconSize) // spare space for the add button
|
||||
box.Show()
|
||||
|
||||
add, _ := gtk.ButtonNewFromIconName("list-add-symbolic", gtk.ICON_SIZE_BUTTON)
|
||||
add.SetRelief(gtk.RELIEF_NONE)
|
||||
add.SetSizeRequest(IconSize, IconSize)
|
||||
add.SetHAlign(gtk.ALIGN_END)
|
||||
add.Show()
|
||||
|
||||
// Do jank stuff to overlay the add button on top of our button.
|
||||
overlay, _ := gtk.OverlayNew()
|
||||
overlay.Add(box)
|
||||
overlay.AddOverlay(add)
|
||||
overlay.Show()
|
||||
|
||||
reveal, _ := gtk.ToggleButtonNew()
|
||||
reveal.Add(overlay)
|
||||
reveal.SetRelief(gtk.RELIEF_NONE)
|
||||
reveal.SetMode(true)
|
||||
reveal.Show()
|
||||
// Add the button overlay into the main button.
|
||||
buttonoverlay.Take(b, add, IconSize)
|
||||
|
||||
// Construct a menu and its items.
|
||||
var menu = menu.NewLazyMenu(reveal)
|
||||
var menu = menu.NewLazyMenu(b)
|
||||
if configurator, ok := svc.(config.Configurator); ok {
|
||||
menu.AddItems(config.MenuItem(configurator))
|
||||
}
|
||||
|
||||
return &header{reveal, box, l, i, add, menu}
|
||||
}
|
||||
|
||||
func (h *header) GetText() string {
|
||||
return h.label.GetText()
|
||||
return &header{b, add, menu}
|
||||
}
|
||||
|
|
|
@ -123,6 +123,10 @@ func NewContainer(svc cchat.Service, ctrl Controller) *Container {
|
|||
return container
|
||||
}
|
||||
|
||||
func (c *Container) GetService() cchat.Service {
|
||||
return c.Service
|
||||
}
|
||||
|
||||
func (c *Container) Sessions() []*session.Row {
|
||||
return c.children.Sessions()
|
||||
}
|
||||
|
|
185
internal/ui/service/session/commander/commander.go
Normal file
185
internal/ui/service/session/commander/commander.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package commander
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/autoscroll"
|
||||
"github.com/google/shlex"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SessionCommander interface {
|
||||
cchat.Session
|
||||
cchat.Commander
|
||||
}
|
||||
|
||||
type Buffer struct {
|
||||
*gtk.TextBuffer
|
||||
svcname string
|
||||
cmder SessionCommander
|
||||
}
|
||||
|
||||
// NewBuffer creates a new buffer with the given SessionCommander, or returns
|
||||
// nil if cmder is nil.
|
||||
func NewBuffer(svc cchat.Service, cmder SessionCommander) *Buffer {
|
||||
if cmder == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, _ := gtk.TextBufferNew(nil)
|
||||
b.CreateTag("error", map[string]interface{}{
|
||||
"foreground": "#FF0000",
|
||||
})
|
||||
return &Buffer{b, svc.Name().Content, cmder}
|
||||
}
|
||||
|
||||
// WriteError is not thread-safe.
|
||||
func (b *Buffer) WriteError(err error) {
|
||||
b.InsertWithTagByName(b.GetEndIter(), err.Error()+"\n", "error")
|
||||
}
|
||||
|
||||
// WriteUnsafe is not thread-safe.
|
||||
func (b *Buffer) WriteUnsafe(bytes []byte) {
|
||||
b.Insert(b.GetEndIter(), string(bytes))
|
||||
}
|
||||
|
||||
// Printlnf is not thread-safe.
|
||||
func (b *Buffer) Printlnf(f string, v ...interface{}) {
|
||||
b.WriteUnsafe([]byte(fmt.Sprintf(f+"\n", v...)))
|
||||
}
|
||||
|
||||
// Write is thread-safe.
|
||||
func (b *Buffer) Write(bytes []byte) (int, error) {
|
||||
gts.ExecAsync(func() { b.WriteUnsafe(bytes) })
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func (b *Buffer) ShowDialog() {
|
||||
SpawnDialog(b)
|
||||
}
|
||||
|
||||
var entryCSS = primitives.PrepareCSS(`
|
||||
* {
|
||||
font-family: monospace;
|
||||
border-radius: 0;
|
||||
}
|
||||
`)
|
||||
|
||||
type Session struct {
|
||||
*gtk.Box
|
||||
words []string
|
||||
cmder cchat.Commander
|
||||
buffer *Buffer
|
||||
}
|
||||
|
||||
func SpawnDialog(buf *Buffer) {
|
||||
s := NewSession(buf.cmder, buf)
|
||||
s.Show()
|
||||
|
||||
h, _ := gtk.HeaderBarNew()
|
||||
h.SetTitle(fmt.Sprintf(
|
||||
"Commander: %s on %s",
|
||||
buf.cmder.Name().Content, buf.svcname,
|
||||
))
|
||||
h.SetShowCloseButton(true)
|
||||
h.Show()
|
||||
|
||||
d, _ := gts.NewEmptyModalDialog()
|
||||
d.SetDefaultSize(450, 250)
|
||||
d.SetTitlebar(h)
|
||||
d.Add(s)
|
||||
d.Show()
|
||||
}
|
||||
|
||||
func NewSession(cmder cchat.Commander, buf *Buffer) *Session {
|
||||
v, _ := gtk.TextViewNewWithBuffer(buf.TextBuffer)
|
||||
v.SetEditable(false)
|
||||
v.SetProperty("monospace", true)
|
||||
v.SetBorderWidth(8)
|
||||
v.SetPixelsAboveLines(1)
|
||||
v.SetWrapMode(gtk.WRAP_WORD_CHAR)
|
||||
v.Show()
|
||||
|
||||
s := autoscroll.NewScrolledWindow()
|
||||
s.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
s.Add(v)
|
||||
s.Show()
|
||||
|
||||
i, _ := gtk.EntryNew()
|
||||
primitives.AttachCSS(i, entryCSS)
|
||||
i.Show()
|
||||
|
||||
b, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
b.PackStart(s, true, true, 0)
|
||||
b.PackStart(i, false, false, 0)
|
||||
|
||||
session := &Session{
|
||||
Box: b,
|
||||
cmder: cmder,
|
||||
buffer: buf,
|
||||
}
|
||||
|
||||
i.Connect("activate", session.inputActivate)
|
||||
// Split words on typing to provide live errors.
|
||||
i.Connect("changed", func(i *gtk.Entry) {
|
||||
t, _ := i.GetText()
|
||||
|
||||
w, err := shlex.Split(t)
|
||||
if err != nil {
|
||||
i.SetIconFromIconName(gtk.ENTRY_ICON_SECONDARY, "dialog-error")
|
||||
i.SetIconTooltipText(gtk.ENTRY_ICON_SECONDARY, err.Error())
|
||||
session.words = nil
|
||||
} else {
|
||||
i.SetIconFromIconName(gtk.ENTRY_ICON_SECONDARY, "")
|
||||
session.words = w
|
||||
}
|
||||
})
|
||||
|
||||
// Focus on the input by default.
|
||||
i.GrabFocus()
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
func (s *Session) inputActivate(e *gtk.Entry) {
|
||||
// If the input is empty, then ignore.
|
||||
if len(s.words) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := s.cmder.RunCommand(s.words)
|
||||
if err != nil {
|
||||
s.buffer.WriteError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Clear the entry.
|
||||
e.SetText("")
|
||||
|
||||
var then = time.Now()
|
||||
s.buffer.Printlnf("%s: Running command...", then.Format(time.Kitchen))
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(s.buffer, r)
|
||||
r.Close()
|
||||
|
||||
gts.ExecAsync(func() {
|
||||
if err != nil {
|
||||
s.buffer.WriteError(errors.Wrap(err, "Internal error"))
|
||||
}
|
||||
|
||||
var now = time.Now()
|
||||
s.buffer.Printlnf(
|
||||
"%s: Finished running command, took %s.",
|
||||
now.Format(time.Kitchen),
|
||||
now.Sub(then).String(),
|
||||
)
|
||||
})
|
||||
}()
|
||||
}
|
|
@ -89,12 +89,12 @@ func (r *Row) Reset() {
|
|||
|
||||
// Remove the children container itself.
|
||||
r.Box.Remove(r.children)
|
||||
|
||||
// Reset the state.
|
||||
r.loaded = false
|
||||
r.serverList = nil
|
||||
r.children = nil
|
||||
}
|
||||
|
||||
// Reset the state.
|
||||
r.loaded = false
|
||||
r.serverList = nil
|
||||
r.children = nil
|
||||
}
|
||||
|
||||
// SetLoading is called by the parent struct.
|
||||
|
|
|
@ -6,10 +6,13 @@ import (
|
|||
"github.com/diamondburned/cchat-gtk/internal/keyring"
|
||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/buttonoverlay"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/breadcrumb"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/menu"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/commander"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
||||
"github.com/diamondburned/cchat/text"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -17,6 +20,8 @@ const IconSize = 32
|
|||
|
||||
// Controller extends server.RowController to add session.
|
||||
type Controller interface {
|
||||
// GetService asks the controller for its service.
|
||||
GetService() cchat.Service
|
||||
// OnSessionDisconnect is called before a session is disconnected. This
|
||||
// function is used for cleanups.
|
||||
OnSessionDisconnect(*Row)
|
||||
|
@ -42,6 +47,9 @@ type Row struct {
|
|||
sessionID string // used for reconnection
|
||||
|
||||
ctrl Controller
|
||||
|
||||
cmder *commander.Buffer
|
||||
cmdbtn *gtk.Button
|
||||
}
|
||||
|
||||
func New(parent breadcrumb.Breadcrumber, ses cchat.Session, ctrl Controller) *Row {
|
||||
|
@ -58,14 +66,36 @@ func NewLoading(parent breadcrumb.Breadcrumber, id, name string, ctrl Controller
|
|||
}
|
||||
|
||||
func newRow(parent breadcrumb.Breadcrumber, name text.Rich, ctrl Controller) *Row {
|
||||
// Bind the row to .session in CSS.
|
||||
row := server.NewRow(parent, name)
|
||||
row.Button.SetPlaceholderIcon("user-invisible-symbolic", IconSize)
|
||||
row.Show()
|
||||
primitives.AddClass(row, "session")
|
||||
primitives.AddClass(row, "server-list")
|
||||
srow := server.NewRow(parent, name)
|
||||
srow.Button.SetPlaceholderIcon("user-invisible-symbolic", IconSize)
|
||||
srow.Show()
|
||||
|
||||
return &Row{Row: row, ctrl: ctrl}
|
||||
// Bind the row to .session in CSS.
|
||||
primitives.AddClass(srow, "session")
|
||||
primitives.AddClass(srow, "server-list")
|
||||
|
||||
// Make a commander button that's hidden by default in case.
|
||||
cmdbtn, _ := gtk.ButtonNewFromIconName("utilities-terminal-symbolic", gtk.ICON_SIZE_BUTTON)
|
||||
buttonoverlay.Take(srow.Button, cmdbtn, server.IconSize)
|
||||
|
||||
row := &Row{
|
||||
Row: srow,
|
||||
ctrl: ctrl,
|
||||
cmdbtn: cmdbtn,
|
||||
}
|
||||
|
||||
cmdbtn.Connect("clicked", row.ShowCommander)
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
// Reset extends the server row's Reset function and resets additional states.
|
||||
// It resets all states back to nil, but the session ID stays.
|
||||
func (r *Row) Reset() {
|
||||
r.Row.Reset()
|
||||
r.Session = nil
|
||||
r.cmder = nil
|
||||
r.cmdbtn.Hide()
|
||||
}
|
||||
|
||||
// RemoveSession removes itself from the session list.
|
||||
|
@ -162,6 +192,16 @@ func (r *Row) SetSession(ses cchat.Session) {
|
|||
r.SetLabelUnsafe(ses.Name())
|
||||
r.SetIconer(ses)
|
||||
|
||||
// Set the commander, if any. The function will return nil if the assertion
|
||||
// returns nil. As such, we assert with an ignored ok bool, allowing cmd to
|
||||
// be nil.
|
||||
cmd, _ := ses.(commander.SessionCommander)
|
||||
r.cmder = commander.NewBuffer(r.ctrl.GetService(), cmd)
|
||||
// Show the command button if the session actually supports the commander.
|
||||
if r.cmder != nil {
|
||||
r.cmdbtn.Show()
|
||||
}
|
||||
|
||||
// Bind extra menu items before loading. These items won't be clickable
|
||||
// during loading.
|
||||
r.SetNormalExtraMenu([]menu.Item{
|
||||
|
@ -183,3 +223,12 @@ func (r *Row) RowSelected(server *server.ServerRow, smsg cchat.ServerMessage) {
|
|||
func (r *Row) BindMover(id string) {
|
||||
primitives.BindDragSortable(r.Button, "GTK_TOGGLE_BUTTON", id, r.ctrl.MoveSession)
|
||||
}
|
||||
|
||||
// ShowCommander shows the commander dialog, or it does nothing if session does
|
||||
// not implement commander.
|
||||
func (r *Row) ShowCommander() {
|
||||
if r.cmder == nil {
|
||||
return
|
||||
}
|
||||
r.cmder.ShowDialog()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue