mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-12-22 12:16:46 +00:00
Slight Gtk fixes and improvements
This commit uses the GtkFileChooserNative over Gtk's own. It also slightly refactors the message container API and fixes minor UI appearances, especially adding the separator.
This commit is contained in:
parent
cf4f8ce245
commit
f5ba082b86
2
go.mod
2
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-20200816224505-3cd69b83a48a
|
||||
replace github.com/gotk3/gotk3 => github.com/gotk3/gotk3 v0.5.1-0.20201028052159-952547abf55a
|
||||
|
||||
require (
|
||||
github.com/Xuanwo/go-locale v1.0.0
|
||||
|
|
2
go.sum
2
go.sum
|
@ -234,6 +234,8 @@ github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
|
|||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotk3/gotk3 v0.5.1-0.20201028052159-952547abf55a h1:9O8VeGmNRqh8UPYLfjYc+W3Gu7vSVTo2uEswq4FO9xI=
|
||||
github.com/gotk3/gotk3 v0.5.1-0.20201028052159-952547abf55a/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
|
|
|
@ -226,11 +226,10 @@ func RenderPixbuf(img image.Image) *gdk.Pixbuf {
|
|||
}
|
||||
|
||||
func SpawnUploader(dirpath string, callback func(absolutePaths []string)) {
|
||||
dialog, _ := gtk.FileChooserDialogNewWith2Buttons(
|
||||
dialog, _ := gtk.FileChooserNativeDialogNew(
|
||||
"Upload File", App.Window,
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
"Cancel", gtk.RESPONSE_CANCEL,
|
||||
"Upload", gtk.RESPONSE_ACCEPT,
|
||||
"Upload", "Cancel",
|
||||
)
|
||||
|
||||
App.Throttler.Connect(dialog)
|
||||
|
@ -248,9 +247,7 @@ func SpawnUploader(dirpath string, callback func(absolutePaths []string)) {
|
|||
dialog.SetCurrentFolder(dirpath)
|
||||
dialog.SetSelectMultiple(true)
|
||||
|
||||
defer dialog.Close()
|
||||
|
||||
if res := dialog.Run(); res != gtk.RESPONSE_ACCEPT {
|
||||
if res := dialog.Run(); res != int(gtk.RESPONSE_ACCEPT) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -259,12 +256,12 @@ func SpawnUploader(dirpath string, callback func(absolutePaths []string)) {
|
|||
}
|
||||
|
||||
// BindPreviewer binds the file chooser dialog with a previewer.
|
||||
func BindPreviewer(fc *gtk.FileChooserDialog) {
|
||||
func BindPreviewer(fc *gtk.FileChooserNativeDialog) {
|
||||
img, _ := gtk.ImageNew()
|
||||
|
||||
fc.SetPreviewWidget(img)
|
||||
fc.Connect("update-preview",
|
||||
func(fc *gtk.FileChooserDialog, img *gtk.Image) {
|
||||
func(_ interface{}, img *gtk.Image) {
|
||||
file := fc.GetPreviewFilename()
|
||||
|
||||
b, err := gdk.PixbufNewFromFileAtScale(file, 256, 256, true)
|
||||
|
|
|
@ -16,7 +16,6 @@ type State struct {
|
|||
}
|
||||
|
||||
type Connector interface {
|
||||
gtk.IWidget
|
||||
Connect(string, interface{}, ...interface{}) (glib.SignalHandle, error)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package compact
|
|||
|
||||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/input"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
|
@ -17,6 +18,18 @@ func NewContainer(ctrl container.Controller) *Container {
|
|||
return &Container{c}
|
||||
}
|
||||
|
||||
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||
gts.ExecAsync(func() { c.GridContainer.CreateMessageUnsafe(msg) })
|
||||
}
|
||||
|
||||
func (c *Container) UpdateMessage(msg cchat.MessageUpdate) {
|
||||
gts.ExecAsync(func() { c.GridContainer.UpdateMessageUnsafe(msg) })
|
||||
}
|
||||
|
||||
func (c *Container) DeleteMessage(msg cchat.MessageDelete) {
|
||||
gts.ExecAsync(func() { c.GridContainer.DeleteMessageUnsafe(msg) })
|
||||
}
|
||||
|
||||
type constructor struct{}
|
||||
|
||||
func (constructor) NewMessage(msg cchat.MessageCreate) container.GridMessage {
|
||||
|
|
|
@ -2,7 +2,6 @@ package container
|
|||
|
||||
import (
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"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/primitives/menu"
|
||||
|
@ -34,9 +33,12 @@ type Container interface {
|
|||
gtk.IWidget
|
||||
|
||||
// Thread-safe methods.
|
||||
cchat.MessagesContainer
|
||||
// cchat.MessagesContainer
|
||||
|
||||
// Thread-unsafe methods.
|
||||
|
||||
// CreateMessageUnsafe creates a new message and returns the index that is
|
||||
// the location the message is added to.
|
||||
CreateMessageUnsafe(cchat.MessageCreate)
|
||||
UpdateMessageUnsafe(cchat.MessageUpdate)
|
||||
DeleteMessageUnsafe(cchat.MessageDelete)
|
||||
|
@ -111,15 +113,3 @@ func (c *GridContainer) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
|||
c.DeleteEarliest(c.MessagesLen() - BacklogLimit)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GridContainer) CreateMessage(msg cchat.MessageCreate) {
|
||||
gts.ExecAsync(func() { c.CreateMessageUnsafe(msg) })
|
||||
}
|
||||
|
||||
func (c *GridContainer) UpdateMessage(msg cchat.MessageUpdate) {
|
||||
gts.ExecAsync(func() { c.UpdateMessageUnsafe(msg) })
|
||||
}
|
||||
|
||||
func (c *GridContainer) DeleteMessage(msg cchat.MessageDelete) {
|
||||
gts.ExecAsync(func() { c.DeleteMessageUnsafe(msg) })
|
||||
}
|
||||
|
|
|
@ -121,7 +121,8 @@ func (c *Container) reuseAvatar(authorID, avatarURL string, full *FullMessage) {
|
|||
}
|
||||
|
||||
func (c *Container) lastMessageIsAuthor(id string, offset int) bool {
|
||||
var last = c.GridStore.NthMessage(c.GridStore.MessagesLen() - (1 + offset))
|
||||
// Get the offfsetth message from last.
|
||||
var last = c.GridStore.NthMessage((c.GridStore.MessagesLen() - 1) + offset)
|
||||
return last != nil && last.AuthorID() == id
|
||||
}
|
||||
|
||||
|
@ -131,33 +132,42 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
|||
// wipe old messages.
|
||||
c.GridContainer.CreateMessageUnsafe(msg)
|
||||
|
||||
// Should we collapse this message? Yes, if the current message's author
|
||||
// is the same as the last author.
|
||||
if c.lastMessageIsAuthor(msg.Author().ID(), 1) {
|
||||
c.compact(c.GridContainer.LastMessage())
|
||||
}
|
||||
|
||||
// See if we need to collapse the second message.
|
||||
if sec := c.NthMessage(1); sec != nil {
|
||||
// If the author isn't the same, then ignore.
|
||||
if sec.AuthorID() != msg.Author().ID() {
|
||||
return
|
||||
}
|
||||
|
||||
// The author is the same; collapse.
|
||||
c.compact(sec)
|
||||
}
|
||||
|
||||
// Did the handler wipe old messages? It will only do so if the user is
|
||||
// scrolled to the bottom.
|
||||
if !c.Bottomed() {
|
||||
// If we're not at the bottom, then we exit.
|
||||
return
|
||||
if c.Bottomed() {
|
||||
// We need to uncollapse the first (top) message. No length check is
|
||||
// needed here, as we just inserted a message.
|
||||
c.uncompact(c.FirstMessage())
|
||||
}
|
||||
|
||||
// We need to uncollapse the first (top) message. No length check is
|
||||
// needed here, as we just inserted a message.
|
||||
c.uncompact(c.FirstMessage())
|
||||
switch msg.ID() {
|
||||
// Should we collapse this message? Yes, if the current message is
|
||||
// inserted at the end and its author is the same as the last author.
|
||||
case c.GridContainer.LastMessage().ID():
|
||||
if c.lastMessageIsAuthor(msg.Author().ID(), -1) {
|
||||
c.compact(c.GridContainer.LastMessage())
|
||||
}
|
||||
|
||||
// If we've prepended the message, then see if we need to collapse the
|
||||
// second message.
|
||||
case c.GridContainer.FirstMessage().ID():
|
||||
if sec := c.NthMessage(1); sec != nil {
|
||||
// If the author isn't the same, then ignore.
|
||||
if sec.AuthorID() != msg.Author().ID() {
|
||||
return
|
||||
}
|
||||
|
||||
// The author is the same; collapse.
|
||||
c.compact(sec)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Container) UpdateMessage(msg cchat.MessageUpdate) {
|
||||
gts.ExecAsync(func() {
|
||||
c.UpdateMessageUnsafe(msg)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -208,12 +208,20 @@ func (c *GridStore) NthMessage(n int) GridMessage {
|
|||
|
||||
// FirstMessage returns the first message.
|
||||
func (c *GridStore) FirstMessage() GridMessage {
|
||||
return c.NthMessage(0)
|
||||
if c.messageList.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Long unwrap.
|
||||
return c.messageList.Front().Value.(*gridMessage).GridMessage
|
||||
}
|
||||
|
||||
// LastMessage returns the latest message.
|
||||
func (c *GridStore) LastMessage() GridMessage {
|
||||
return c.NthMessage(c.MessagesLen() - 1)
|
||||
if c.messageList.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Long unwrap.
|
||||
return c.messageList.Back().Value.(*gridMessage).GridMessage
|
||||
}
|
||||
|
||||
// Message finds the message state in the container. It is not thread-safe. This
|
||||
|
@ -273,17 +281,21 @@ func (c *GridStore) AddPresendMessage(msg input.PresendMessage) PresendGridMessa
|
|||
return presend
|
||||
}
|
||||
|
||||
// Many attempts were made to have CreateMessageUnsafe return an index. That is
|
||||
// unreliable. The index might be off if the message buffer is cleaned up. Don't
|
||||
// rely on it.
|
||||
|
||||
func (c *GridStore) CreateMessageUnsafe(msg cchat.MessageCreate) {
|
||||
// Call the event handler last.
|
||||
defer c.Controller.AuthorEvent(msg.Author())
|
||||
|
||||
// Attempt to update before insertion (aka upsert).
|
||||
if msgc := c.Message(msg.ID(), msg.Nonce()); msgc != nil {
|
||||
if msgc := c.message(msg.ID(), msg.Nonce()); msgc != nil {
|
||||
msgc.UpdateAuthor(msg.Author())
|
||||
msgc.UpdateContent(msg.Content(), false)
|
||||
msgc.UpdateTimestamp(msg.Time())
|
||||
|
||||
c.Controller.BindMenu(msgc)
|
||||
c.Controller.BindMenu(msgc.GridMessage)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ func (f *Field) SetMessenger(session cchat.Session, messenger cchat.Messenger) {
|
|||
f.typing = f.Messenger.AsTypingIndicator()
|
||||
|
||||
// See if we can upload files.
|
||||
f.SetAllowUpload(f.Sender.CanAttach())
|
||||
f.SetAllowUpload(f.Sender != nil && f.Sender.CanAttach())
|
||||
|
||||
// Populate the duration state if typer is not nil.
|
||||
if f.typing != nil {
|
||||
|
|
|
@ -52,6 +52,12 @@ type Controller interface {
|
|||
OnMessageDone()
|
||||
}
|
||||
|
||||
type MessagesContainer interface {
|
||||
gtk.IWidget
|
||||
container.Container
|
||||
cchat.MessagesContainer
|
||||
}
|
||||
|
||||
type View struct {
|
||||
*gtk.Box
|
||||
|
||||
|
@ -66,7 +72,7 @@ type View struct {
|
|||
|
||||
MsgBox *gtk.Box
|
||||
Typing *typing.Container
|
||||
Container container.Container
|
||||
Container MessagesContainer
|
||||
contType int // msgIndex
|
||||
|
||||
MemberList *memberlist.Container // right box
|
||||
|
|
|
@ -2,7 +2,6 @@ package primitives
|
|||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||
|
@ -279,24 +278,7 @@ func InlineCSS(ctx StyleContexter, css string) {
|
|||
// LeafletOnFold binds a callback to a leaflet that would be called when the
|
||||
// leaflet's folded state changes.
|
||||
func LeafletOnFold(leaflet *handy.Leaflet, foldedFn func(folded bool)) {
|
||||
var lastFold = leaflet.GetFolded()
|
||||
foldedFn(lastFold)
|
||||
|
||||
// Give each callback a 500ms wait for animations to complete.
|
||||
const dt = 500 * time.Millisecond
|
||||
var last = time.Now()
|
||||
|
||||
leaflet.ConnectAfter("size-allocate", func() {
|
||||
// Ignore if this event is too recent.
|
||||
if now := time.Now(); now.Add(-dt).Before(last) {
|
||||
return
|
||||
} else {
|
||||
last = now
|
||||
}
|
||||
|
||||
if folded := leaflet.GetFolded(); folded != lastFold {
|
||||
lastFold = folded
|
||||
foldedFn(folded)
|
||||
}
|
||||
leaflet.ConnectAfter("notify::folded", func() {
|
||||
foldedFn(leaflet.GetFolded())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/diamondburned/handy"
|
||||
"github.com/gotk3/gotk3/gdk"
|
||||
"github.com/gotk3/gotk3/glib"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -83,10 +84,19 @@ func NewApplication() *App {
|
|||
app.HeaderGroup.AddHeaderBar(&app.Services.Header.HeaderBar)
|
||||
app.HeaderGroup.AddHeaderBar(&app.MessageView.Header.HeaderBar)
|
||||
|
||||
separator, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
||||
separator.Show()
|
||||
|
||||
app.Leaflet = *handy.LeafletNew()
|
||||
app.Leaflet.SetTransitionType(handy.LeafletTransitionTypeUnder)
|
||||
app.Leaflet.SetChildTransitionDuration(75)
|
||||
app.Leaflet.SetTransitionType(handy.LeafletTransitionTypeSlide)
|
||||
app.Leaflet.SetCanSwipeBack(true)
|
||||
|
||||
app.Leaflet.Add(app.Services)
|
||||
app.Leaflet.Add(separator)
|
||||
app.Leaflet.Add(app.MessageView)
|
||||
|
||||
app.Leaflet.ChildSetProperty(separator, "navigatable", false)
|
||||
app.Leaflet.Show()
|
||||
|
||||
// Bind the preferences action for our GAction button in the header popover.
|
||||
|
|
22
shell.nix
22
shell.nix
|
@ -1,12 +1,26 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
let libhandy = pkgs.libhandy.overrideAttrs(old: {
|
||||
name = "libhandy-1.0.1";
|
||||
src = builtins.fetchGit {
|
||||
url = "https://gitlab.gnome.org/GNOME/libhandy.git";
|
||||
rev = "5cee0927b8b39dea1b2a62ec6d19169f73ba06c6";
|
||||
};
|
||||
patches = [];
|
||||
|
||||
buildInputs = old.buildInputs ++ (with pkgs; [
|
||||
gnome3.librsvg
|
||||
gdk-pixbuf
|
||||
]);
|
||||
});
|
||||
|
||||
in pkgs.stdenv.mkDerivation rec {
|
||||
name = "cchat-gtk";
|
||||
version = "0.0.2";
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
libhandy gnome3.gspell gnome3.glib gnome3.gtk
|
||||
];
|
||||
buildInputs = [ libhandy ] ++ (with pkgs; [
|
||||
gnome3.gspell gnome3.glib gnome3.gtk
|
||||
]);
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkgconfig go
|
||||
|
|
Loading…
Reference in a new issue