cchat-gtk/internal/ui/ui.go

245 lines
6.4 KiB
Go
Raw Normal View History

2020-05-26 06:51:06 +00:00
package ui
import (
"github.com/diamondburned/cchat"
2020-07-16 05:41:21 +00:00
"github.com/diamondburned/cchat-gtk/icons"
2020-05-26 06:51:06 +00:00
"github.com/diamondburned/cchat-gtk/internal/gts"
2020-06-20 07:28:47 +00:00
"github.com/diamondburned/cchat-gtk/internal/log"
2020-06-20 04:40:34 +00:00
"github.com/diamondburned/cchat-gtk/internal/ui/config/preferences"
"github.com/diamondburned/cchat-gtk/internal/ui/messages"
2020-12-30 08:31:03 +00:00
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
2020-06-04 23:00:41 +00:00
"github.com/diamondburned/cchat-gtk/internal/ui/service"
"github.com/diamondburned/cchat-gtk/internal/ui/service/auth"
"github.com/diamondburned/cchat-gtk/internal/ui/service/session"
2020-05-28 19:26:55 +00:00
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
2020-08-28 07:16:03 +00:00
"github.com/diamondburned/handy"
2020-07-16 05:41:21 +00:00
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
2020-06-20 07:28:47 +00:00
"github.com/pkg/errors"
2020-05-26 06:51:06 +00:00
)
2020-06-06 00:47:28 +00:00
func init() {
// Load the local CSS.
2020-07-16 05:41:21 +00:00
gts.LoadCSS("main", `
/* Make CSS more consistent across themes */
headerbar { padding-left: 0 }
/* .appmenu { margin: 0 20px } */
2020-07-16 05:41:21 +00:00
popover > *:not(stack):not(button) { margin: 6px }
/* Hack to fix the input bar being high in Adwaita */
.input-field * { min-height: 0 }
/* Hide all scroll undershoots */
undershoot { background-size: 0 }
2020-07-16 05:41:21 +00:00
`)
2020-06-06 00:47:28 +00:00
}
// constraints for the left panel
2020-08-31 04:47:58 +00:00
const leftCurrentWidth = 300
func clamp(n, min, max int) int {
switch {
case n > max:
return max
case n < min:
return min
default:
return n
}
}
2020-05-26 06:51:06 +00:00
2020-06-04 23:00:41 +00:00
type App struct {
2020-08-28 07:16:03 +00:00
handy.Leaflet
HeaderGroup *handy.HeaderGroup
Services *service.View
MessageView *messages.View
2020-06-04 23:00:41 +00:00
// used to keep track of what row to disconnect before switching
lastSelector func(bool)
2020-05-26 06:51:06 +00:00
}
var (
2020-08-28 07:16:03 +00:00
_ gts.MainApplication = (*App)(nil)
_ service.Controller = (*App)(nil)
_ messages.Controller = (*App)(nil)
2020-05-26 06:51:06 +00:00
)
2020-06-04 23:00:41 +00:00
func NewApplication() *App {
app := &App{}
2020-05-26 06:51:06 +00:00
2020-08-28 07:16:03 +00:00
app.Services = service.NewView(app)
app.Services.SetSizeRequest(leftCurrentWidth, -1)
app.Services.SetHExpand(false)
2020-08-28 07:16:03 +00:00
app.Services.Show()
2020-08-28 07:16:03 +00:00
app.MessageView = messages.NewView(app)
app.MessageView.SetHExpand(true)
2020-08-28 07:16:03 +00:00
app.MessageView.Show()
app.HeaderGroup = handy.HeaderGroupNew()
app.HeaderGroup.AddHeaderBar(&app.Services.Header.HeaderBar)
app.HeaderGroup.AddHeaderBar(&app.MessageView.Header.HeaderBar)
separator, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
separator.Show()
2020-08-28 07:16:03 +00:00
app.Leaflet = *handy.LeafletNew()
app.Leaflet.SetChildTransitionDuration(75)
app.Leaflet.SetTransitionType(handy.LeafletTransitionTypeSlide)
app.Leaflet.SetCanSwipeBack(true)
2020-08-28 07:16:03 +00:00
app.Leaflet.Add(app.Services)
app.Leaflet.Add(separator)
2020-08-28 07:16:03 +00:00
app.Leaflet.Add(app.MessageView)
app.Leaflet.ChildSetProperty(separator, "navigatable", false)
2020-08-28 07:16:03 +00:00
app.Leaflet.Show()
2020-06-20 04:40:34 +00:00
// Bind the preferences action for our GAction button in the header popover.
// The action name for this is "app.preferences".
gts.AddAppAction("preferences", preferences.SpawnPreferenceDialog)
2020-12-30 08:00:18 +00:00
// We should assert folded state based on the window's width instead of the
// leaflet's state, since doing that might cause a feedback loop.
const minWidth = 450
var foldedState bool
app.Leaflet.Connect("size-allocate", func(leaflet *handy.Leaflet) {
folded := leaflet.GetAllocatedWidth() < minWidth
if foldedState != folded {
foldedState = folded
app.MessageView.SetFolded(folded)
}
})
2020-12-30 08:31:03 +00:00
// We'd still want to control the visibility of the back button when we
// fold, however.
primitives.LeafletOnFold(&app.Leaflet,
app.MessageView.Header.SetShowBackButton)
2020-05-26 06:51:06 +00:00
return app
}
2020-08-28 07:16:03 +00:00
// Services methods.
2020-06-04 23:00:41 +00:00
func (app *App) AddService(svc cchat.Service) {
2020-08-28 07:16:03 +00:00
app.Services.AddService(svc)
2020-06-07 04:27:28 +00:00
}
// OnSessionRemove resets things before the session is removed.
2020-07-16 05:41:21 +00:00
func (app *App) OnSessionRemove(s *service.Service, r *session.Row) {
// Reset the message view if it's what we're showing.
2020-08-28 07:16:03 +00:00
if app.MessageView.SessionID() == r.ID() {
app.MessageView.Reset()
}
2020-05-28 19:26:55 +00:00
}
2020-07-16 05:41:21 +00:00
func (app *App) OnSessionDisconnect(s *service.Service, r *session.Row) {
// We're basically doing the same thing as removing a session. Check
// OnSessionRemove above.
2020-07-16 05:41:21 +00:00
app.OnSessionRemove(s, r)
}
func (app *App) SessionSelected(svc *service.Service, ses *session.Row) {
2020-07-19 01:57:57 +00:00
// Is there an old row that we should deactivate?
if app.lastSelector != nil {
app.lastSelector(false)
app.lastSelector = nil
}
2020-08-27 19:14:24 +00:00
// TODO
// reset view when setservers top level called
// TODO: restore last message box
2020-08-28 07:16:03 +00:00
app.MessageView.Reset()
}
func (app *App) ClearMessenger(ses *session.Row) {
2021-01-05 02:05:33 +00:00
// No need to try if the window is destroyed already, since its children
// will also be destroyed.
if !gts.IsClosing() && app.MessageView.SessionID() == ses.Session.ID() {
app.MessageView.Reset()
}
}
2020-10-15 06:32:11 +00:00
func (app *App) MessengerSelected(ses *session.Row, srv *server.ServerRow) {
// Change to the message view.
app.Leaflet.SetVisibleChild(app.MessageView)
// Assert that the new server is not the same one.
if app.MessageView.SessionID() == ses.Session.ID() &&
app.MessageView.ServerID() == srv.Server.ID() {
return
}
// Is there an old row that we should deactivate?
if app.lastSelector != nil {
app.lastSelector(false)
2020-06-04 23:00:41 +00:00
}
// Set the new row.
app.lastSelector = srv.SetSelected
app.lastSelector(true)
2020-06-04 23:00:41 +00:00
2020-10-15 06:32:11 +00:00
app.MessageView.JoinServer(ses.Session, srv.Server, srv)
2020-08-20 23:53:13 +00:00
}
2020-08-28 07:16:03 +00:00
// MessageView methods.
func (app *App) GoBack() {
app.Leaflet.Navigate(handy.NavigationDirectionBack)
}
2020-08-20 23:53:13 +00:00
func (app *App) OnMessageBusy() {
// Disable the server list because we don't want the user to switch around
// while we're loading.
2020-11-06 04:01:00 +00:00
app.Services.SetSensitive(false)
2020-08-20 23:53:13 +00:00
}
2020-08-20 23:53:13 +00:00
func (app *App) OnMessageDone() {
// Re-enable the server list.
2020-11-06 04:01:00 +00:00
app.Services.SetSensitive(true)
2020-06-04 23:00:41 +00:00
}
func (app *App) AuthenticateSession(list *service.List, ssvc *service.Service) {
var svc = ssvc.Service()
2020-06-04 23:00:41 +00:00
auth.NewDialog(svc.Name(), svc.Authenticate(), func(ses cchat.Session) {
ssvc.AddSession(ses)
2020-06-04 23:00:41 +00:00
})
2020-05-26 06:51:06 +00:00
}
2020-06-20 07:28:47 +00:00
// Close is called when the application finishes gracefully.
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.
2020-08-28 07:16:03 +00:00
for _, s := range app.Services.Services.Services {
var service = s.Service().Name()
for _, session := range s.BodyList.Sessions() {
2020-06-20 07:28:47 +00:00
if session.Session == nil {
continue
}
log.Printlnf("Disconnecting %s session %s", service, session.ID())
2020-06-20 07:28:47 +00:00
if err := session.Session.Disconnect(); err != nil {
log.Error(errors.Wrap(err, "Failed to disconnect "+session.ID()))
}
2020-06-20 04:40:34 +00:00
}
}
}
2020-07-16 05:41:21 +00:00
func (app *App) Icon() *gdk.Pixbuf {
return icons.Logo256Pixbuf()
2020-07-16 05:41:21 +00:00
}
func (app *App) Menu() *glib.MenuModel {
2020-08-28 07:16:03 +00:00
return app.Services.Header.MenuModel
2020-07-16 05:41:21 +00:00
}