mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-03-21 09:29:20 +00:00
WIP libhandy support
This commit is contained in:
parent
c2fc4d7512
commit
ba105295eb
2
go.mod
2
go.mod
|
@ -10,7 +10,7 @@ require (
|
||||||
github.com/diamondburned/cchat v0.0.49
|
github.com/diamondburned/cchat v0.0.49
|
||||||
github.com/diamondburned/cchat-discord v0.0.0-20200821041521-647c854d7b5e
|
github.com/diamondburned/cchat-discord v0.0.0-20200821041521-647c854d7b5e
|
||||||
github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b
|
github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b
|
||||||
github.com/diamondburned/handy v0.0.0-20200827040421-5b4a15843526 // indirect
|
github.com/diamondburned/handy v0.0.0-20200827040421-5b4a15843526
|
||||||
github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972
|
github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/goodsign/monday v1.0.0
|
github.com/goodsign/monday v1.0.0
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/diamondburned/cchat-gtk/internal/gts/throttler"
|
"github.com/diamondburned/cchat-gtk/internal/gts/throttler"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||||
|
"github.com/diamondburned/handy"
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"github.com/gotk3/gotk3/gdk"
|
"github.com/gotk3/gotk3/gdk"
|
||||||
"github.com/gotk3/gotk3/glib"
|
"github.com/gotk3/gotk3/glib"
|
||||||
|
@ -21,12 +22,22 @@ var Args = append([]string{}, os.Args...)
|
||||||
|
|
||||||
var App struct {
|
var App struct {
|
||||||
*gtk.Application
|
*gtk.Application
|
||||||
Window *gtk.ApplicationWindow
|
Window *handy.ApplicationWindow
|
||||||
Header *gtk.HeaderBar
|
|
||||||
|
|
||||||
Throttler *throttler.State
|
Throttler *throttler.State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windower is the interface for a window.
|
||||||
|
type Windower interface {
|
||||||
|
gtk.IWidget
|
||||||
|
gtk.IWindow
|
||||||
|
throttler.Connector
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddWindow(w Windower) {
|
||||||
|
App.AddWindow(w)
|
||||||
|
App.Throttler.Connect(w)
|
||||||
|
}
|
||||||
|
|
||||||
// Clipboard is initialized on init().
|
// Clipboard is initialized on init().
|
||||||
var Clipboard *gtk.Clipboard
|
var Clipboard *gtk.Clipboard
|
||||||
|
|
||||||
|
@ -39,7 +50,8 @@ func NewModalDialog() (*gtk.Dialog, error) {
|
||||||
}
|
}
|
||||||
d.SetModal(true)
|
d.SetModal(true)
|
||||||
d.SetTransientFor(App.Window)
|
d.SetTransientFor(App.Window)
|
||||||
App.Throttler.Connect(d)
|
|
||||||
|
AddWindow(d)
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
@ -75,72 +87,42 @@ func init() {
|
||||||
App.Throttler = throttler.Bind(App.Application)
|
App.Throttler = throttler.Bind(App.Application)
|
||||||
}
|
}
|
||||||
|
|
||||||
// // AppMenuWidget returns the box that holds the app menu.
|
type MainApplication interface {
|
||||||
// func AppMenuWidget() (widget *gtk.Widget) {
|
gtk.IWidget
|
||||||
// App.Header.For().Foreach(func(v interface{}) {
|
|
||||||
// // If we've already found the widget, then stop finding.
|
|
||||||
// if widget != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Cast the interface to a widget.
|
|
||||||
// curr := v.(gtk.IWidget).ToWidget()
|
|
||||||
|
|
||||||
// log.Println("testing")
|
|
||||||
|
|
||||||
// // Check if the widget has a class named "left".
|
|
||||||
// if sctx, _ := curr.GetStyleContext(); sctx.HasClass("left") {
|
|
||||||
// log.Println("has class .left")
|
|
||||||
// widget = curr
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
type Window interface {
|
|
||||||
Window() gtk.IWidget
|
|
||||||
Header() gtk.IWidget
|
|
||||||
Menu() *glib.MenuModel
|
Menu() *glib.MenuModel
|
||||||
Icon() *gdk.Pixbuf
|
Icon() *gdk.Pixbuf
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main(wfn func() Window) {
|
func Main(wfn func() MainApplication) {
|
||||||
App.Application.Connect("activate", func() {
|
App.Application.Connect("activate", func() {
|
||||||
|
handy.Init()
|
||||||
|
|
||||||
// Load all CSS onto the default screen.
|
// Load all CSS onto the default screen.
|
||||||
loadProviders(getDefaultScreen())
|
loadProviders(getDefaultScreen())
|
||||||
|
|
||||||
App.Header, _ = gtk.HeaderBarNew()
|
// App.Header, _ = gtk.HeaderBarNew()
|
||||||
// Right buttons only.
|
// // Right buttons only.
|
||||||
App.Header.SetDecorationLayout(":minimize,close")
|
// App.Header.SetDecorationLayout(":minimize,close")
|
||||||
App.Header.SetShowCloseButton(true)
|
// App.Header.SetShowCloseButton(true)
|
||||||
App.Header.SetProperty("spacing", 0)
|
// App.Header.SetProperty("spacing", 0)
|
||||||
|
|
||||||
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
App.Window = handy.ApplicationWindowNew()
|
||||||
App.Header.SetCustomTitle(b)
|
|
||||||
|
|
||||||
App.Window, _ = gtk.ApplicationWindowNew(App.Application)
|
|
||||||
App.Window.SetDefaultSize(1000, 500)
|
App.Window.SetDefaultSize(1000, 500)
|
||||||
App.Window.SetTitlebar(App.Header)
|
App.Window.Show()
|
||||||
|
AddWindow(&App.Window.Window)
|
||||||
|
|
||||||
|
App.Throttler.Connect(&App.Window.Window)
|
||||||
|
|
||||||
// Execute the function later, because we need it to run after
|
// Execute the function later, because we need it to run after
|
||||||
// initialization.
|
// initialization.
|
||||||
w := wfn()
|
w := wfn()
|
||||||
App.Application.SetAppMenu(w.Menu())
|
App.Window.Add(w)
|
||||||
|
|
||||||
App.Window.SetIcon(w.Icon())
|
App.Window.SetIcon(w.Icon())
|
||||||
App.Window.Add(w.Window())
|
// App.Application.SetAppMenu(w.Menu())
|
||||||
App.Window.Show()
|
|
||||||
|
|
||||||
App.Header.Add(w.Header())
|
|
||||||
App.Header.Show()
|
|
||||||
|
|
||||||
// Connect extra actions.
|
|
||||||
AddAppAction("quit", App.Window.Destroy)
|
|
||||||
|
|
||||||
// Connect the destructor.
|
// Connect the destructor.
|
||||||
App.Window.Connect("destroy", func() {
|
App.Window.Window.Connect("destroy", func() {
|
||||||
// Hide the application window.
|
// Hide the application window.
|
||||||
App.Window.Hide()
|
App.Window.Hide()
|
||||||
|
|
||||||
|
@ -154,6 +136,9 @@ func Main(wfn func() Window) {
|
||||||
w.Close()
|
w.Close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Connect extra actions.
|
||||||
|
AddAppAction("quit", App.Window.Destroy)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use a special function to run the application. Exit with the appropriate
|
// Use a special function to run the application. Exit with the appropriate
|
||||||
|
|
|
@ -9,43 +9,33 @@ import (
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/actions"
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/actions"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||||
|
"github.com/diamondburned/handy"
|
||||||
"github.com/gotk3/gotk3/glib"
|
"github.com/gotk3/gotk3/glib"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/gotk3/gotk3/pango"
|
"github.com/gotk3/gotk3/pango"
|
||||||
)
|
)
|
||||||
|
|
||||||
type header struct {
|
type header struct {
|
||||||
*gtk.Box
|
|
||||||
left *headerLeft // middle-ish
|
left *headerLeft // middle-ish
|
||||||
right *headerRight
|
right *headerRight
|
||||||
menu *glib.Menu
|
menu *glib.Menu
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHeader() *header {
|
func newHeader() *header {
|
||||||
left := newHeaderLeft()
|
|
||||||
left.Show()
|
|
||||||
|
|
||||||
right := newHeaderRight()
|
|
||||||
right.Show()
|
|
||||||
|
|
||||||
separator, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
|
||||||
separator.Show()
|
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
|
||||||
box.PackStart(left, false, false, 0)
|
|
||||||
box.PackStart(separator, false, false, 0)
|
|
||||||
box.PackStart(right, true, true, 0)
|
|
||||||
box.Show()
|
|
||||||
|
|
||||||
menu := glib.MenuNew()
|
menu := glib.MenuNew()
|
||||||
menu.Append("Preferences", "app.preferences")
|
menu.Append("Preferences", "app.preferences")
|
||||||
menu.Append("Quit", "app.quit")
|
menu.Append("Quit", "app.quit")
|
||||||
|
|
||||||
|
left := newHeaderLeft()
|
||||||
left.appmenu.SetMenuModel(&menu.MenuModel)
|
left.appmenu.SetMenuModel(&menu.MenuModel)
|
||||||
|
|
||||||
// TODO
|
right := newHeaderRight()
|
||||||
|
|
||||||
|
group := handy.HeaderGroupNew()
|
||||||
|
group.AddHeaderBar(&left.HeaderBar)
|
||||||
|
group.AddHeaderBar(&right.HeaderBar)
|
||||||
|
|
||||||
return &header{
|
return &header{
|
||||||
box,
|
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
menu,
|
menu,
|
||||||
|
@ -111,60 +101,84 @@ func (a *appMenu) SetSizeRequest(w, h int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type headerLeft struct {
|
type headerLeft struct {
|
||||||
*gtk.Box
|
handy.HeaderBar
|
||||||
|
|
||||||
appmenu *appMenu
|
appmenu *appMenu
|
||||||
svcname *gtk.Label
|
svcname *gtk.Label
|
||||||
sesmenu *actions.MenuButton
|
sesmenu *actions.MenuButton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serviceNameCSS = primitives.PrepareClassCSS("service-name", `
|
||||||
|
.service-name {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var sessionMenuCSS = primitives.PrepareClassCSS("session-menu", `
|
||||||
|
.session-menu {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
func newHeaderLeft() *headerLeft {
|
func newHeaderLeft() *headerLeft {
|
||||||
appmenu := newAppMenu()
|
appmenu := newAppMenu()
|
||||||
appmenu.Show()
|
appmenu.Show()
|
||||||
|
|
||||||
sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
// sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
||||||
sep.Show()
|
// sep.Show()
|
||||||
primitives.AddClass(sep, "titlebutton")
|
// primitives.AddClass(sep, "titlebutton")
|
||||||
|
|
||||||
svcname, _ := gtk.LabelNew("")
|
svcname, _ := gtk.LabelNew("cchat-gtk")
|
||||||
svcname.SetXAlign(0)
|
svcname.SetXAlign(0)
|
||||||
svcname.SetEllipsize(pango.ELLIPSIZE_END)
|
svcname.SetEllipsize(pango.ELLIPSIZE_END)
|
||||||
svcname.Show()
|
svcname.Show()
|
||||||
svcname.SetMarginStart(14)
|
serviceNameCSS(svcname)
|
||||||
|
|
||||||
sesmenu := actions.NewMenuButton()
|
sesmenu := actions.NewMenuButton()
|
||||||
sesmenu.Show()
|
sesmenu.Show()
|
||||||
|
sessionMenuCSS(sesmenu)
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
header := handy.HeaderBarNew()
|
||||||
box.PackStart(appmenu, false, false, 0)
|
header.SetShowCloseButton(true)
|
||||||
box.PackStart(sep, false, false, 0)
|
header.PackStart(appmenu)
|
||||||
box.PackStart(svcname, true, true, 0)
|
// box.PackStart(sep, false, false, 0)
|
||||||
box.PackStart(sesmenu, false, false, 5)
|
header.PackStart(svcname)
|
||||||
|
header.PackStart(sesmenu)
|
||||||
|
|
||||||
return &headerLeft{
|
return &headerLeft{
|
||||||
Box: box,
|
HeaderBar: *header,
|
||||||
appmenu: appmenu,
|
appmenu: appmenu,
|
||||||
svcname: svcname,
|
svcname: svcname,
|
||||||
sesmenu: sesmenu,
|
sesmenu: sesmenu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type headerRight struct {
|
type headerRight struct {
|
||||||
*gtk.Box
|
handy.HeaderBar
|
||||||
|
|
||||||
breadcrumb *gtk.Label
|
breadcrumb *gtk.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rightBreadcrumbCSS = primitives.PrepareClassCSS("right-breadcrumb", `
|
||||||
|
.right-breadcrumb {
|
||||||
|
margin: 0 14px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
func newHeaderRight() *headerRight {
|
func newHeaderRight() *headerRight {
|
||||||
bc, _ := gtk.LabelNew(BreadcrumbSlash)
|
bc, _ := gtk.LabelNew(BreadcrumbSlash)
|
||||||
bc.SetUseMarkup(true)
|
bc.SetUseMarkup(true)
|
||||||
bc.SetXAlign(0.0)
|
bc.SetXAlign(0.0)
|
||||||
bc.Show()
|
bc.Show()
|
||||||
|
rightBreadcrumbCSS(bc)
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
header := handy.HeaderBarNew()
|
||||||
box.PackStart(bc, true, true, 14)
|
header.SetShowCloseButton(true)
|
||||||
box.Show()
|
header.PackStart(bc)
|
||||||
|
header.Show()
|
||||||
|
|
||||||
return &headerRight{
|
return &headerRight{
|
||||||
Box: box,
|
HeaderBar: *header,
|
||||||
breadcrumb: bc,
|
breadcrumb: bc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
64
internal/ui/messages/header.go
Normal file
64
internal/ui/messages/header.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||||
|
"github.com/diamondburned/handy"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// const BreadcrumbSlash = `<span rise="-1024" size="x-large">❭</span>`
|
||||||
|
const BreadcrumbSlash = " 〉"
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
handy.HeaderBar
|
||||||
|
|
||||||
|
Breadcrumb *gtk.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
var rightBreadcrumbCSS = primitives.PrepareClassCSS("right-breadcrumb", `
|
||||||
|
.right-breadcrumb {
|
||||||
|
margin: 0 14px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func NewHeader() *Header {
|
||||||
|
bc, _ := gtk.LabelNew(BreadcrumbSlash)
|
||||||
|
bc.SetUseMarkup(true)
|
||||||
|
bc.SetXAlign(0.0)
|
||||||
|
bc.Show()
|
||||||
|
rightBreadcrumbCSS(bc)
|
||||||
|
|
||||||
|
header := handy.HeaderBarNew()
|
||||||
|
header.SetShowCloseButton(true)
|
||||||
|
header.PackStart(bc)
|
||||||
|
header.Show()
|
||||||
|
|
||||||
|
return &Header{
|
||||||
|
HeaderBar: *header,
|
||||||
|
Breadcrumb: bc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) Reset() {
|
||||||
|
h.SetBreadcrumber(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) SetBreadcrumber(b traverse.Breadcrumber) {
|
||||||
|
if b == nil {
|
||||||
|
h.Breadcrumb.SetText("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var crumb = b.Breadcrumb()
|
||||||
|
for i := range crumb {
|
||||||
|
crumb[i] = html.EscapeString(crumb[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Breadcrumb.SetMarkup(
|
||||||
|
BreadcrumbSlash + " " + strings.Join(crumb, " "+BreadcrumbSlash+" "),
|
||||||
|
)
|
||||||
|
}
|
|
@ -49,8 +49,12 @@ type Controller interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type View struct {
|
type View struct {
|
||||||
*sadface.FaceView
|
*gtk.Box
|
||||||
Grid *gtk.Grid
|
|
||||||
|
Header *Header
|
||||||
|
|
||||||
|
FaceView *sadface.FaceView
|
||||||
|
Grid *gtk.Grid
|
||||||
|
|
||||||
Scroller *autoscroll.ScrolledWindow
|
Scroller *autoscroll.ScrolledWindow
|
||||||
InputView *input.InputView
|
InputView *input.InputView
|
||||||
|
@ -126,6 +130,15 @@ func NewView(c Controller) *View {
|
||||||
logo.Show()
|
logo.Show()
|
||||||
|
|
||||||
view.FaceView = sadface.New(view.Grid, logo)
|
view.FaceView = sadface.New(view.Grid, logo)
|
||||||
|
view.FaceView.Show()
|
||||||
|
|
||||||
|
view.Header = NewHeader()
|
||||||
|
view.Header.Show()
|
||||||
|
|
||||||
|
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
|
view.Box.PackStart(view.Header, false, false, 0)
|
||||||
|
view.Box.PackStart(view.FaceView, true, true, 0)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +166,7 @@ func (v *View) createMessageContainer() {
|
||||||
func (v *View) Bottomed() bool { return v.Scroller.Bottomed }
|
func (v *View) Bottomed() bool { return v.Scroller.Bottomed }
|
||||||
|
|
||||||
func (v *View) Reset() {
|
func (v *View) Reset() {
|
||||||
|
v.Header.Reset() // Reset the header.
|
||||||
v.state.Reset() // Reset the state variables.
|
v.state.Reset() // Reset the state variables.
|
||||||
v.Typing.Reset() // Reset the typing state.
|
v.Typing.Reset() // Reset the typing state.
|
||||||
v.InputView.Reset() // Reset the input.
|
v.InputView.Reset() // Reset the input.
|
||||||
|
@ -194,7 +208,7 @@ func (v *View) JoinServer(session cchat.Session, server ServerMessage) {
|
||||||
err = errors.Wrap(err, "Failed to join server")
|
err = errors.Wrap(err, "Failed to join server")
|
||||||
// Even if we're erroring out, we're running the done() callback
|
// Even if we're erroring out, we're running the done() callback
|
||||||
// anyway.
|
// anyway.
|
||||||
return func() { v.ctrl.OnMessageDone(); v.SetError(err) }, err
|
return func() { v.ctrl.OnMessageDone(); v.FaceView.SetError(err) }, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
|
|
|
@ -14,6 +14,7 @@ type MenuButton struct {
|
||||||
|
|
||||||
func NewMenuButton() *MenuButton {
|
func NewMenuButton() *MenuButton {
|
||||||
b, _ := gtk.MenuButtonNew()
|
b, _ := gtk.MenuButtonNew()
|
||||||
|
b.SetVAlign(gtk.ALIGN_CENTER)
|
||||||
b.SetSensitive(false)
|
b.SetSensitive(false)
|
||||||
|
|
||||||
return &MenuButton{
|
return &MenuButton{
|
||||||
|
|
137
internal/ui/service/header.go
Normal file
137
internal/ui/service/header.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/diamondburned/cchat-gtk/icons"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/actions"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||||
|
"github.com/diamondburned/handy"
|
||||||
|
"github.com/gotk3/gotk3/glib"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
"github.com/gotk3/gotk3/pango"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppMenu struct {
|
||||||
|
gtk.MenuButton
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAppMenu() *AppMenu {
|
||||||
|
img, _ := gtk.ImageNew()
|
||||||
|
img.SetFromPixbuf(icons.Logo256(24))
|
||||||
|
img.Show()
|
||||||
|
|
||||||
|
appmenu, _ := gtk.MenuButtonNew()
|
||||||
|
appmenu.SetImage(img)
|
||||||
|
appmenu.SetUsePopover(true)
|
||||||
|
appmenu.SetHAlign(gtk.ALIGN_CENTER)
|
||||||
|
appmenu.SetMarginStart(8)
|
||||||
|
appmenu.SetMarginEnd(8)
|
||||||
|
|
||||||
|
return &AppMenu{*appmenu}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppMenu) SetSizeRequest(w, h int) {
|
||||||
|
// Subtract the margin size.
|
||||||
|
if w -= 8 * 2; w < 0 {
|
||||||
|
w = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
a.MenuButton.SetSizeRequest(w, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
handy.HeaderBar
|
||||||
|
|
||||||
|
MenuModel *glib.MenuModel
|
||||||
|
|
||||||
|
AppMenu *AppMenu
|
||||||
|
SvcName *gtk.Label
|
||||||
|
SesMenu *actions.MenuButton
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceNameCSS = primitives.PrepareClassCSS("service-name", `
|
||||||
|
.service-name {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var sessionMenuCSS = primitives.PrepareClassCSS("session-menu", `
|
||||||
|
.session-menu {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func NewHeader() *Header {
|
||||||
|
menu := glib.MenuNew()
|
||||||
|
menu.Append("Preferences", "app.preferences")
|
||||||
|
menu.Append("Quit", "app.quit")
|
||||||
|
|
||||||
|
appmenu := NewAppMenu()
|
||||||
|
appmenu.Show()
|
||||||
|
appmenu.SetMenuModel(&menu.MenuModel)
|
||||||
|
|
||||||
|
sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
||||||
|
sep.Show()
|
||||||
|
primitives.AddClass(sep, "titlebutton")
|
||||||
|
|
||||||
|
svcname, _ := gtk.LabelNew("cchat-gtk")
|
||||||
|
svcname.SetXAlign(0)
|
||||||
|
svcname.SetEllipsize(pango.ELLIPSIZE_END)
|
||||||
|
svcname.Show()
|
||||||
|
serviceNameCSS(svcname)
|
||||||
|
|
||||||
|
sesmenu := actions.NewMenuButton()
|
||||||
|
sesmenu.Show()
|
||||||
|
sessionMenuCSS(sesmenu)
|
||||||
|
|
||||||
|
header := handy.HeaderBarNew()
|
||||||
|
header.SetProperty("spacing", 0)
|
||||||
|
header.SetShowCloseButton(true)
|
||||||
|
header.PackStart(appmenu)
|
||||||
|
header.PackStart(sep)
|
||||||
|
header.PackStart(svcname)
|
||||||
|
header.PackStart(sesmenu)
|
||||||
|
|
||||||
|
// Hack to hide the title.
|
||||||
|
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
|
header.SetCustomTitle(b)
|
||||||
|
|
||||||
|
return &Header{
|
||||||
|
HeaderBar: *header,
|
||||||
|
MenuModel: &menu.MenuModel,
|
||||||
|
AppMenu: appmenu,
|
||||||
|
SvcName: svcname,
|
||||||
|
SesMenu: sesmenu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) SetBreadcrumber(b traverse.Breadcrumber) {
|
||||||
|
if b == nil {
|
||||||
|
h.SvcName.SetText("cchat-gtk")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if crumb := b.Breadcrumb(); len(crumb) > 0 {
|
||||||
|
h.SvcName.SetText(crumb[0])
|
||||||
|
} else {
|
||||||
|
h.SvcName.SetText("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) SetSessionMenu(s *session.Row) {
|
||||||
|
h.SesMenu.Bind(s.ActionsMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sizeBinder interface {
|
||||||
|
primitives.Connector
|
||||||
|
GetAllocatedWidth() int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sizeBinder = (*List)(nil)
|
||||||
|
|
||||||
|
func (h *Header) AppMenuBindSize(c sizeBinder) {
|
||||||
|
c.Connect("size-allocate", func() {
|
||||||
|
h.AppMenu.SetSizeRequest(c.GetAllocatedWidth(), -1)
|
||||||
|
})
|
||||||
|
}
|
|
@ -45,7 +45,6 @@ func NewList(vctl ViewController) *List {
|
||||||
svlist.ListBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
svlist.ListBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
svlist.ListBox.Show()
|
svlist.ListBox.Show()
|
||||||
svlist.ListBox.SetHAlign(gtk.ALIGN_START)
|
svlist.ListBox.SetHAlign(gtk.ALIGN_START)
|
||||||
svlist.ListBox.SetHExpand(false)
|
|
||||||
listCSS(svlist.ListBox)
|
listCSS(svlist.ListBox)
|
||||||
|
|
||||||
svlist.ScrolledWindow, _ = gtk.ScrolledWindowNew(nil, nil)
|
svlist.ScrolledWindow, _ = gtk.ScrolledWindowNew(nil, nil)
|
||||||
|
|
|
@ -8,13 +8,14 @@ import (
|
||||||
"github.com/diamondburned/cchat-gtk/internal/humanize"
|
"github.com/diamondburned/cchat-gtk/internal/humanize"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/spinner"
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/spinner"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server/traverse"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/gotk3/gotk3/pango"
|
"github.com/gotk3/gotk3/pango"
|
||||||
)
|
)
|
||||||
|
|
||||||
const FaceSize = 48 // gtk.ICON_SIZE_DIALOG
|
const FaceSize = 48 // gtk.ICON_SIZE_DIALOG
|
||||||
|
const ListWidth = 200
|
||||||
|
|
||||||
// Servers wraps around a list of servers inherited from Children. It's the
|
// Servers wraps around a list of servers inherited from Children. It's the
|
||||||
// container that's displayed on the right of the service sidebar.
|
// container that's displayed on the right of the service sidebar.
|
||||||
|
@ -34,7 +35,7 @@ var toplevelCSS = primitives.PrepareClassCSS("top-level", `
|
||||||
func NewServers(p traverse.Breadcrumber, ctrl server.Controller) *Servers {
|
func NewServers(p traverse.Breadcrumber, ctrl server.Controller) *Servers {
|
||||||
c := server.NewChildren(p, ctrl)
|
c := server.NewChildren(p, ctrl)
|
||||||
c.SetMarginStart(0) // children is top level; there is no main row
|
c.SetMarginStart(0) // children is top level; there is no main row
|
||||||
c.SetHExpand(true) // fill
|
c.SetVExpand(true)
|
||||||
c.Show()
|
c.Show()
|
||||||
toplevelCSS(c)
|
toplevelCSS(c)
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,12 @@ type Controller interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type View struct {
|
type View struct {
|
||||||
*gtk.Box // 2 panes, but left-most hard-coded
|
*gtk.Box
|
||||||
Controller // inherit main controller
|
|
||||||
|
Header *Header
|
||||||
|
|
||||||
|
BottomPane *gtk.Box // 2 panes, but left-most hard-coded
|
||||||
|
Controller // inherit main controller
|
||||||
|
|
||||||
Services *List
|
Services *List
|
||||||
ServerView *gtk.ScrolledWindow
|
ServerView *gtk.ScrolledWindow
|
||||||
|
@ -40,9 +44,9 @@ func NewView(ctrller Controller) *View {
|
||||||
view.Services = NewList(view)
|
view.Services = NewList(view)
|
||||||
view.Services.Show()
|
view.Services.Show()
|
||||||
|
|
||||||
// Make a separator.
|
view.Header = NewHeader()
|
||||||
// sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
view.Header.AppMenuBindSize(view.Services)
|
||||||
// sep.Show()
|
view.Header.Show()
|
||||||
|
|
||||||
// Make a stack for the middle panel.
|
// Make a stack for the middle panel.
|
||||||
view.ServerStack = singlestack.NewStack()
|
view.ServerStack = singlestack.NewStack()
|
||||||
|
@ -50,18 +54,21 @@ func NewView(ctrller Controller) *View {
|
||||||
view.ServerStack.SetTransitionDuration(50)
|
view.ServerStack.SetTransitionDuration(50)
|
||||||
view.ServerStack.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE)
|
view.ServerStack.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE)
|
||||||
view.ServerStack.SetHomogeneous(true)
|
view.ServerStack.SetHomogeneous(true)
|
||||||
view.ServerStack.SetHExpand(true)
|
|
||||||
view.ServerStack.Show()
|
view.ServerStack.Show()
|
||||||
|
|
||||||
view.ServerView, _ = gtk.ScrolledWindowNew(nil, nil)
|
view.ServerView, _ = gtk.ScrolledWindowNew(nil, nil)
|
||||||
view.ServerView.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
view.ServerView.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||||
view.ServerView.SetHExpand(true)
|
|
||||||
view.ServerView.Add(view.ServerStack)
|
view.ServerView.Add(view.ServerStack)
|
||||||
view.ServerView.Show()
|
view.ServerView.Show()
|
||||||
|
|
||||||
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
view.BottomPane, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
view.Box.PackStart(view.Services, false, false, 0)
|
view.BottomPane.PackStart(view.Services, false, false, 0)
|
||||||
view.Box.PackStart(view.ServerView, true, true, 0)
|
view.BottomPane.PackStart(view.ServerView, true, true, 0)
|
||||||
|
view.BottomPane.Show()
|
||||||
|
|
||||||
|
view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
|
view.Box.PackStart(view.Header, false, false, 0)
|
||||||
|
view.Box.PackStart(view.BottomPane, true, true, 0)
|
||||||
view.Box.Show()
|
view.Box.Show()
|
||||||
|
|
||||||
return view
|
return view
|
||||||
|
@ -88,6 +95,24 @@ func (v *View) SessionSelected(svc *Service, srow *session.Row) {
|
||||||
// reference anyway. In fact, cchat REQUIRES us to do so.
|
// reference anyway. In fact, cchat REQUIRES us to do so.
|
||||||
v.ServerStack.SetVisibleChild(srow.Servers)
|
v.ServerStack.SetVisibleChild(srow.Servers)
|
||||||
|
|
||||||
// Call the controller's method.
|
v.Header.SetSessionMenu(srow)
|
||||||
|
v.Header.SetBreadcrumber(srow)
|
||||||
v.Controller.SessionSelected(svc, srow)
|
v.Controller.SessionSelected(svc, srow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RowSelected is called when a row is selected. It updates the header then
|
||||||
|
// calls the application's RowSelected method.
|
||||||
|
func (v *View) RowSelected(srow *session.Row, srv *server.ServerRow, smsg cchat.ServerMessage) {
|
||||||
|
v.Header.SetBreadcrumber(srv)
|
||||||
|
v.Controller.RowSelected(srow, srv, smsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) OnSessionRemove(s *Service, r *session.Row) {
|
||||||
|
v.Header.SetBreadcrumber(nil)
|
||||||
|
v.Controller.OnSessionRemove(s, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) OnSessionDisconnect(s *Service, r *session.Row) {
|
||||||
|
v.Header.SetBreadcrumber(nil)
|
||||||
|
v.Controller.OnSessionDisconnect(s, r)
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/auth"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/auth"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
||||||
|
"github.com/diamondburned/handy"
|
||||||
"github.com/gotk3/gotk3/gdk"
|
"github.com/gotk3/gotk3/gdk"
|
||||||
"github.com/gotk3/gotk3/glib"
|
"github.com/gotk3/gotk3/glib"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,36 +51,40 @@ func clamp(n, min, max int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
window *window
|
handy.Leaflet
|
||||||
header *header
|
HeaderGroup *handy.HeaderGroup
|
||||||
|
|
||||||
|
Services *service.View
|
||||||
|
MessageView *messages.View
|
||||||
|
|
||||||
// used to keep track of what row to disconnect before switching
|
// used to keep track of what row to disconnect before switching
|
||||||
lastSelector func(bool)
|
lastSelector func(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ gts.Window = (*App)(nil)
|
_ gts.MainApplication = (*App)(nil)
|
||||||
_ service.Controller = (*App)(nil)
|
_ service.Controller = (*App)(nil)
|
||||||
|
_ messages.Controller = (*App)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApplication() *App {
|
func NewApplication() *App {
|
||||||
app := &App{}
|
app := &App{}
|
||||||
app.window = newWindow(app)
|
|
||||||
app.header = newHeader()
|
|
||||||
|
|
||||||
// Resize the app icon with the left-most sidebar.
|
app.Services = service.NewView(app)
|
||||||
services := app.window.Services.Services
|
app.Services.SetSizeRequest(leftMinWidth, -1)
|
||||||
services.Connect("size-allocate", func() {
|
app.Services.Show()
|
||||||
app.header.left.appmenu.SetSizeRequest(services.GetAllocatedWidth(), -1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Resize the left-side header w/ the left-side pane.
|
app.MessageView = messages.NewView(app)
|
||||||
app.window.Services.ServerView.Connect("size-allocate", func() {
|
app.MessageView.Show()
|
||||||
// Get the current width of the left sidebar.
|
|
||||||
width := app.window.GetPosition()
|
app.HeaderGroup = handy.HeaderGroupNew()
|
||||||
// Set the left-side header's size.
|
app.HeaderGroup.AddHeaderBar(&app.Services.Header.HeaderBar)
|
||||||
app.header.left.SetSizeRequest(width, -1)
|
app.HeaderGroup.AddHeaderBar(&app.MessageView.Header.HeaderBar)
|
||||||
})
|
|
||||||
|
app.Leaflet = *handy.LeafletNew()
|
||||||
|
app.Leaflet.Add(app.Services)
|
||||||
|
app.Leaflet.Add(app.MessageView)
|
||||||
|
app.Leaflet.Show()
|
||||||
|
|
||||||
// Bind the preferences action for our GAction button in the header popover.
|
// Bind the preferences action for our GAction button in the header popover.
|
||||||
// The action name for this is "app.preferences".
|
// The action name for this is "app.preferences".
|
||||||
|
@ -89,16 +93,17 @@ func NewApplication() *App {
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Services methods.
|
||||||
|
|
||||||
func (app *App) AddService(svc cchat.Service) {
|
func (app *App) AddService(svc cchat.Service) {
|
||||||
app.window.Services.AddService(svc)
|
app.Services.AddService(svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSessionRemove resets things before the session is removed.
|
// OnSessionRemove resets things before the session is removed.
|
||||||
func (app *App) OnSessionRemove(s *service.Service, r *session.Row) {
|
func (app *App) OnSessionRemove(s *service.Service, r *session.Row) {
|
||||||
// Reset the message view if it's what we're showing.
|
// Reset the message view if it's what we're showing.
|
||||||
if app.window.MessageView.SessionID() == r.ID() {
|
if app.MessageView.SessionID() == r.ID() {
|
||||||
app.window.MessageView.Reset()
|
app.MessageView.Reset()
|
||||||
app.header.SetBreadcrumber(nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,9 +124,7 @@ func (app *App) SessionSelected(svc *service.Service, ses *session.Row) {
|
||||||
// reset view when setservers top level called
|
// reset view when setservers top level called
|
||||||
|
|
||||||
// TODO: restore last message box
|
// TODO: restore last message box
|
||||||
app.window.MessageView.Reset()
|
app.MessageView.Reset()
|
||||||
app.header.SetBreadcrumber(ses)
|
|
||||||
app.header.SetSessionMenu(ses)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) RowSelected(ses *session.Row, srv *server.ServerRow, smsg cchat.ServerMessage) {
|
func (app *App) RowSelected(ses *session.Row, srv *server.ServerRow, smsg cchat.ServerMessage) {
|
||||||
|
@ -134,12 +137,12 @@ func (app *App) RowSelected(ses *session.Row, srv *server.ServerRow, smsg cchat.
|
||||||
app.lastSelector = srv.SetSelected
|
app.lastSelector = srv.SetSelected
|
||||||
app.lastSelector(true)
|
app.lastSelector(true)
|
||||||
|
|
||||||
app.header.SetBreadcrumber(srv)
|
|
||||||
|
|
||||||
// Assert that server is also a list, then join the server.
|
// Assert that server is also a list, then join the server.
|
||||||
app.window.MessageView.JoinServer(ses.Session, smsg.(messages.ServerMessage))
|
app.MessageView.JoinServer(ses.Session, smsg.(messages.ServerMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageView methods.
|
||||||
|
|
||||||
func (app *App) OnMessageBusy() {
|
func (app *App) OnMessageBusy() {
|
||||||
// Disable the server list because we don't want the user to switch around
|
// Disable the server list because we don't want the user to switch around
|
||||||
// while we're loading.
|
// while we're loading.
|
||||||
|
@ -163,7 +166,7 @@ func (app *App) Close() {
|
||||||
// Disconnect everything. This blocks the main thread, so by the time we're
|
// Disconnect everything. This blocks the main thread, so by the time we're
|
||||||
// done, the application would exit immediately. There's no need to update
|
// done, the application would exit immediately. There's no need to update
|
||||||
// the GUI.
|
// the GUI.
|
||||||
for _, s := range app.window.AllServices() {
|
for _, s := range app.Services.Services.Services {
|
||||||
var service = s.Service().Name()
|
var service = s.Service().Name()
|
||||||
|
|
||||||
for _, session := range s.BodyList.Sessions() {
|
for _, session := range s.BodyList.Sessions() {
|
||||||
|
@ -180,18 +183,10 @@ func (app *App) Close() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) Header() gtk.IWidget {
|
|
||||||
return app.header
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) Window() gtk.IWidget {
|
|
||||||
return app.window
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) Icon() *gdk.Pixbuf {
|
func (app *App) Icon() *gdk.Pixbuf {
|
||||||
return icons.Logo256(0)
|
return icons.Logo256(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) Menu() *glib.MenuModel {
|
func (app *App) Menu() *glib.MenuModel {
|
||||||
return &app.header.menu.MenuModel
|
return app.Services.Header.MenuModel
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages"
|
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service"
|
|
||||||
"github.com/gotk3/gotk3/gtk"
|
|
||||||
)
|
|
||||||
|
|
||||||
type window struct {
|
|
||||||
*gtk.Paned
|
|
||||||
Services *service.View
|
|
||||||
MessageView *messages.View
|
|
||||||
}
|
|
||||||
|
|
||||||
type Controller interface {
|
|
||||||
service.Controller
|
|
||||||
messages.Controller
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindow(mainctl Controller) *window {
|
|
||||||
services := service.NewView(mainctl)
|
|
||||||
services.SetSizeRequest(leftMinWidth, -1)
|
|
||||||
services.Show()
|
|
||||||
|
|
||||||
mesgview := messages.NewView(mainctl)
|
|
||||||
mesgview.Show()
|
|
||||||
|
|
||||||
pane, _ := gtk.PanedNew(gtk.ORIENTATION_HORIZONTAL)
|
|
||||||
pane.Pack1(services, false, false)
|
|
||||||
pane.Pack2(mesgview, true, false)
|
|
||||||
pane.SetPosition(leftCurrentWidth)
|
|
||||||
pane.Show()
|
|
||||||
|
|
||||||
return &window{pane, services, mesgview}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *window) AllServices() []*service.Service {
|
|
||||||
return w.Services.Services.Services
|
|
||||||
}
|
|
||||||
|
|
2
main.go
2
main.go
|
@ -16,7 +16,7 @@ import (
|
||||||
var destructor = func() {}
|
var destructor = func() {}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
gts.Main(func() gts.Window {
|
gts.Main(func() gts.MainApplication {
|
||||||
var app = ui.NewApplication()
|
var app = ui.NewApplication()
|
||||||
|
|
||||||
// Load all cchat services.
|
// Load all cchat services.
|
||||||
|
|
Loading…
Reference in a new issue