diff --git a/go.mod b/go.mod index 9dae34f..8caf47f 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/diamondburned/cchat v0.0.49 github.com/diamondburned/cchat-discord v0.0.0-20200821041521-647c854d7b5e 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/disintegration/imaging v1.6.2 github.com/goodsign/monday v1.0.0 diff --git a/internal/gts/gts.go b/internal/gts/gts.go index cf6d750..982a703 100644 --- a/internal/gts/gts.go +++ b/internal/gts/gts.go @@ -8,6 +8,7 @@ import ( "github.com/diamondburned/cchat-gtk/internal/gts/throttler" "github.com/diamondburned/cchat-gtk/internal/log" + "github.com/diamondburned/handy" "github.com/disintegration/imaging" "github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/glib" @@ -21,12 +22,22 @@ var Args = append([]string{}, os.Args...) var App struct { *gtk.Application - Window *gtk.ApplicationWindow - Header *gtk.HeaderBar - + Window *handy.ApplicationWindow 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(). var Clipboard *gtk.Clipboard @@ -39,7 +50,8 @@ func NewModalDialog() (*gtk.Dialog, error) { } d.SetModal(true) d.SetTransientFor(App.Window) - App.Throttler.Connect(d) + + AddWindow(d) return d, nil } @@ -75,72 +87,42 @@ func init() { App.Throttler = throttler.Bind(App.Application) } -// // AppMenuWidget returns the box that holds the app menu. -// func AppMenuWidget() (widget *gtk.Widget) { -// 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 +type MainApplication interface { + gtk.IWidget Menu() *glib.MenuModel Icon() *gdk.Pixbuf Close() } -func Main(wfn func() Window) { +func Main(wfn func() MainApplication) { App.Application.Connect("activate", func() { + handy.Init() + // Load all CSS onto the default screen. loadProviders(getDefaultScreen()) - App.Header, _ = gtk.HeaderBarNew() - // Right buttons only. - App.Header.SetDecorationLayout(":minimize,close") - App.Header.SetShowCloseButton(true) - App.Header.SetProperty("spacing", 0) + // App.Header, _ = gtk.HeaderBarNew() + // // Right buttons only. + // App.Header.SetDecorationLayout(":minimize,close") + // App.Header.SetShowCloseButton(true) + // App.Header.SetProperty("spacing", 0) - b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) - App.Header.SetCustomTitle(b) - - App.Window, _ = gtk.ApplicationWindowNew(App.Application) + App.Window = handy.ApplicationWindowNew() 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 // initialization. w := wfn() - App.Application.SetAppMenu(w.Menu()) - + App.Window.Add(w) App.Window.SetIcon(w.Icon()) - App.Window.Add(w.Window()) - App.Window.Show() - - App.Header.Add(w.Header()) - App.Header.Show() - - // Connect extra actions. - AddAppAction("quit", App.Window.Destroy) + // App.Application.SetAppMenu(w.Menu()) // Connect the destructor. - App.Window.Connect("destroy", func() { + App.Window.Window.Connect("destroy", func() { // Hide the application window. App.Window.Hide() @@ -154,6 +136,9 @@ func Main(wfn func() Window) { w.Close() }) }) + + // Connect extra actions. + AddAppAction("quit", App.Window.Destroy) }) // Use a special function to run the application. Exit with the appropriate diff --git a/internal/ui/header.go b/internal/ui/header.go index aeabd7a..969532c 100644 --- a/internal/ui/header.go +++ b/internal/ui/header.go @@ -9,43 +9,33 @@ import ( "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 header struct { - *gtk.Box left *headerLeft // middle-ish right *headerRight menu *glib.Menu } 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.Append("Preferences", "app.preferences") menu.Append("Quit", "app.quit") + left := newHeaderLeft() left.appmenu.SetMenuModel(&menu.MenuModel) - // TODO + right := newHeaderRight() + + group := handy.HeaderGroupNew() + group.AddHeaderBar(&left.HeaderBar) + group.AddHeaderBar(&right.HeaderBar) + return &header{ - box, left, right, menu, @@ -111,60 +101,84 @@ func (a *appMenu) SetSizeRequest(w, h int) { } type headerLeft struct { - *gtk.Box + handy.HeaderBar + 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 newHeaderLeft() *headerLeft { appmenu := newAppMenu() appmenu.Show() - sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL) - sep.Show() - primitives.AddClass(sep, "titlebutton") + // sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL) + // sep.Show() + // primitives.AddClass(sep, "titlebutton") - svcname, _ := gtk.LabelNew("") + svcname, _ := gtk.LabelNew("cchat-gtk") svcname.SetXAlign(0) svcname.SetEllipsize(pango.ELLIPSIZE_END) svcname.Show() - svcname.SetMarginStart(14) + serviceNameCSS(svcname) sesmenu := actions.NewMenuButton() sesmenu.Show() + sessionMenuCSS(sesmenu) - box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) - box.PackStart(appmenu, false, false, 0) - box.PackStart(sep, false, false, 0) - box.PackStart(svcname, true, true, 0) - box.PackStart(sesmenu, false, false, 5) + header := handy.HeaderBarNew() + header.SetShowCloseButton(true) + header.PackStart(appmenu) + // box.PackStart(sep, false, false, 0) + header.PackStart(svcname) + header.PackStart(sesmenu) return &headerLeft{ - Box: box, - appmenu: appmenu, - svcname: svcname, - sesmenu: sesmenu, + HeaderBar: *header, + appmenu: appmenu, + svcname: svcname, + sesmenu: sesmenu, } } type headerRight struct { - *gtk.Box + handy.HeaderBar + breadcrumb *gtk.Label } +var rightBreadcrumbCSS = primitives.PrepareClassCSS("right-breadcrumb", ` + .right-breadcrumb { + margin: 0 14px; + } +`) + func newHeaderRight() *headerRight { bc, _ := gtk.LabelNew(BreadcrumbSlash) bc.SetUseMarkup(true) bc.SetXAlign(0.0) bc.Show() + rightBreadcrumbCSS(bc) - box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) - box.PackStart(bc, true, true, 14) - box.Show() + header := handy.HeaderBarNew() + header.SetShowCloseButton(true) + header.PackStart(bc) + header.Show() return &headerRight{ - Box: box, + HeaderBar: *header, breadcrumb: bc, } } diff --git a/internal/ui/messages/header.go b/internal/ui/messages/header.go new file mode 100644 index 0000000..3a6d26c --- /dev/null +++ b/internal/ui/messages/header.go @@ -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 = `` +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+" "), + ) +} diff --git a/internal/ui/messages/view.go b/internal/ui/messages/view.go index e407797..cb98d30 100644 --- a/internal/ui/messages/view.go +++ b/internal/ui/messages/view.go @@ -49,8 +49,12 @@ type Controller interface { } type View struct { - *sadface.FaceView - Grid *gtk.Grid + *gtk.Box + + Header *Header + + FaceView *sadface.FaceView + Grid *gtk.Grid Scroller *autoscroll.ScrolledWindow InputView *input.InputView @@ -126,6 +130,15 @@ func NewView(c Controller) *View { logo.Show() 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 } @@ -153,6 +166,7 @@ func (v *View) createMessageContainer() { func (v *View) Bottomed() bool { return v.Scroller.Bottomed } func (v *View) Reset() { + v.Header.Reset() // Reset the header. v.state.Reset() // Reset the state variables. v.Typing.Reset() // Reset the typing state. 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") // Even if we're erroring out, we're running the done() callback // anyway. - return func() { v.ctrl.OnMessageDone(); v.SetError(err) }, err + return func() { v.ctrl.OnMessageDone(); v.FaceView.SetError(err) }, err } return func() { diff --git a/internal/ui/primitives/actions/menubutton.go b/internal/ui/primitives/actions/menubutton.go index d3bc09d..0622093 100644 --- a/internal/ui/primitives/actions/menubutton.go +++ b/internal/ui/primitives/actions/menubutton.go @@ -14,6 +14,7 @@ type MenuButton struct { func NewMenuButton() *MenuButton { b, _ := gtk.MenuButtonNew() + b.SetVAlign(gtk.ALIGN_CENTER) b.SetSensitive(false) return &MenuButton{ diff --git a/internal/ui/service/header.go b/internal/ui/service/header.go new file mode 100644 index 0000000..57c72bb --- /dev/null +++ b/internal/ui/service/header.go @@ -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) + }) +} diff --git a/internal/ui/service/list.go b/internal/ui/service/list.go index 466c64e..b50cf33 100644 --- a/internal/ui/service/list.go +++ b/internal/ui/service/list.go @@ -45,7 +45,6 @@ func NewList(vctl ViewController) *List { svlist.ListBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) svlist.ListBox.Show() svlist.ListBox.SetHAlign(gtk.ALIGN_START) - svlist.ListBox.SetHExpand(false) listCSS(svlist.ListBox) svlist.ScrolledWindow, _ = gtk.ScrolledWindowNew(nil, nil) diff --git a/internal/ui/service/session/servers.go b/internal/ui/service/session/servers.go index 60d3c98..9d32f89 100644 --- a/internal/ui/service/session/servers.go +++ b/internal/ui/service/session/servers.go @@ -8,13 +8,14 @@ import ( "github.com/diamondburned/cchat-gtk/internal/humanize" "github.com/diamondburned/cchat-gtk/internal/ui/primitives" "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/traverse" "github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/pango" ) const FaceSize = 48 // gtk.ICON_SIZE_DIALOG +const ListWidth = 200 // Servers wraps around a list of servers inherited from Children. It's the // 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 { c := server.NewChildren(p, ctrl) c.SetMarginStart(0) // children is top level; there is no main row - c.SetHExpand(true) // fill + c.SetVExpand(true) c.Show() toplevelCSS(c) diff --git a/internal/ui/service/view.go b/internal/ui/service/view.go index 20c2e55..ad2e7d0 100644 --- a/internal/ui/service/view.go +++ b/internal/ui/service/view.go @@ -23,8 +23,12 @@ type Controller interface { } type View struct { - *gtk.Box // 2 panes, but left-most hard-coded - Controller // inherit main controller + *gtk.Box + + Header *Header + + BottomPane *gtk.Box // 2 panes, but left-most hard-coded + Controller // inherit main controller Services *List ServerView *gtk.ScrolledWindow @@ -40,9 +44,9 @@ func NewView(ctrller Controller) *View { view.Services = NewList(view) view.Services.Show() - // Make a separator. - // sep, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL) - // sep.Show() + view.Header = NewHeader() + view.Header.AppMenuBindSize(view.Services) + view.Header.Show() // Make a stack for the middle panel. view.ServerStack = singlestack.NewStack() @@ -50,18 +54,21 @@ func NewView(ctrller Controller) *View { view.ServerStack.SetTransitionDuration(50) view.ServerStack.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE) view.ServerStack.SetHomogeneous(true) - view.ServerStack.SetHExpand(true) view.ServerStack.Show() view.ServerView, _ = gtk.ScrolledWindowNew(nil, nil) view.ServerView.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) - view.ServerView.SetHExpand(true) view.ServerView.Add(view.ServerStack) view.ServerView.Show() - view.Box, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) - view.Box.PackStart(view.Services, false, false, 0) - view.Box.PackStart(view.ServerView, true, true, 0) + view.BottomPane, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) + view.BottomPane.PackStart(view.Services, false, false, 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() 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. v.ServerStack.SetVisibleChild(srow.Servers) - // Call the controller's method. + v.Header.SetSessionMenu(srow) + v.Header.SetBreadcrumber(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) +} diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 872a274..082404b 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -11,9 +11,9 @@ import ( "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/server" + "github.com/diamondburned/handy" "github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/glib" - "github.com/gotk3/gotk3/gtk" "github.com/pkg/errors" ) @@ -51,36 +51,40 @@ func clamp(n, min, max int) int { } type App struct { - window *window - header *header + handy.Leaflet + HeaderGroup *handy.HeaderGroup + + Services *service.View + MessageView *messages.View // used to keep track of what row to disconnect before switching lastSelector func(bool) } var ( - _ gts.Window = (*App)(nil) - _ service.Controller = (*App)(nil) + _ gts.MainApplication = (*App)(nil) + _ service.Controller = (*App)(nil) + _ messages.Controller = (*App)(nil) ) func NewApplication() *App { app := &App{} - app.window = newWindow(app) - app.header = newHeader() - // Resize the app icon with the left-most sidebar. - services := app.window.Services.Services - services.Connect("size-allocate", func() { - app.header.left.appmenu.SetSizeRequest(services.GetAllocatedWidth(), -1) - }) + app.Services = service.NewView(app) + app.Services.SetSizeRequest(leftMinWidth, -1) + app.Services.Show() - // Resize the left-side header w/ the left-side pane. - app.window.Services.ServerView.Connect("size-allocate", func() { - // Get the current width of the left sidebar. - width := app.window.GetPosition() - // Set the left-side header's size. - app.header.left.SetSizeRequest(width, -1) - }) + app.MessageView = messages.NewView(app) + app.MessageView.Show() + + app.HeaderGroup = handy.HeaderGroupNew() + app.HeaderGroup.AddHeaderBar(&app.Services.Header.HeaderBar) + 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. // The action name for this is "app.preferences". @@ -89,16 +93,17 @@ func NewApplication() *App { return app } +// Services methods. + func (app *App) AddService(svc cchat.Service) { - app.window.Services.AddService(svc) + app.Services.AddService(svc) } // OnSessionRemove resets things before the session is removed. func (app *App) OnSessionRemove(s *service.Service, r *session.Row) { // Reset the message view if it's what we're showing. - if app.window.MessageView.SessionID() == r.ID() { - app.window.MessageView.Reset() - app.header.SetBreadcrumber(nil) + if app.MessageView.SessionID() == r.ID() { + app.MessageView.Reset() } } @@ -119,9 +124,7 @@ func (app *App) SessionSelected(svc *service.Service, ses *session.Row) { // reset view when setservers top level called // TODO: restore last message box - app.window.MessageView.Reset() - app.header.SetBreadcrumber(ses) - app.header.SetSessionMenu(ses) + app.MessageView.Reset() } 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(true) - app.header.SetBreadcrumber(srv) - // 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() { // Disable the server list because we don't want the user to switch around // 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 // done, the application would exit immediately. There's no need to update // the GUI. - for _, s := range app.window.AllServices() { + for _, s := range app.Services.Services.Services { var service = s.Service().Name() 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 { return icons.Logo256(0) } func (app *App) Menu() *glib.MenuModel { - return &app.header.menu.MenuModel + return app.Services.Header.MenuModel } diff --git a/internal/ui/window.go b/internal/ui/window.go index aabc5ef..5b1faa2 100644 --- a/internal/ui/window.go +++ b/internal/ui/window.go @@ -1,39 +1 @@ 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 -} diff --git a/main.go b/main.go index 9fb37c0..cbfab7c 100644 --- a/main.go +++ b/main.go @@ -16,7 +16,7 @@ import ( var destructor = func() {} func main() { - gts.Main(func() gts.Window { + gts.Main(func() gts.MainApplication { var app = ui.NewApplication() // Load all cchat services.