mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2025-03-28 20:59:19 +00:00
Working prototype
This commit is contained in:
parent
275ae709ae
commit
ee657a8177
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Xuanwo/go-locale v0.2.0
|
github.com/Xuanwo/go-locale v0.2.0
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/diamondburned/cchat v0.0.9
|
github.com/diamondburned/cchat v0.0.9
|
||||||
github.com/diamondburned/cchat-mock v0.0.0-20200525222906-807afeffb7d4
|
github.com/diamondburned/cchat-mock v0.0.0-20200525222906-807afeffb7d4
|
||||||
github.com/goodsign/monday v1.0.0
|
github.com/goodsign/monday v1.0.0
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -3,8 +3,8 @@ github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqC
|
||||||
github.com/Xuanwo/go-locale v0.2.0 h1:1N8SGG2VNpLl6VVa8ueZm3Nm+dxvk8ffY9aviKHl4IE=
|
github.com/Xuanwo/go-locale v0.2.0 h1:1N8SGG2VNpLl6VVa8ueZm3Nm+dxvk8ffY9aviKHl4IE=
|
||||||
github.com/Xuanwo/go-locale v0.2.0/go.mod h1:6qbT9M726OJgyiGZro2YwPmx63wQzlH+VvtjJWQoftw=
|
github.com/Xuanwo/go-locale v0.2.0/go.mod h1:6qbT9M726OJgyiGZro2YwPmx63wQzlH+VvtjJWQoftw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/diamondburned/cchat v0.0.8 h1:/PmI23SFHJcjYBWNBwQbp36n7fDvDu+NMnQuhM5FM2E=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/diamondburned/cchat v0.0.8/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/diamondburned/cchat v0.0.9 h1:+F96eDDuaOg4v4dz3GBDWbEW4dZ/k5uGrDp33/yeXR8=
|
github.com/diamondburned/cchat v0.0.9 h1:+F96eDDuaOg4v4dz3GBDWbEW4dZ/k5uGrDp33/yeXR8=
|
||||||
github.com/diamondburned/cchat v0.0.9/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
github.com/diamondburned/cchat v0.0.9/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
github.com/diamondburned/cchat-mock v0.0.0-20200525222906-807afeffb7d4 h1:k6vfDs6NR8yIi2V7YEpNp9Vujtl6mpDL5cWh7Pg09kk=
|
github.com/diamondburned/cchat-mock v0.0.0-20200525222906-807afeffb7d4 h1:k6vfDs6NR8yIi2V7YEpNp9Vujtl6mpDL5cWh7Pg09kk=
|
||||||
|
@ -13,18 +13,22 @@ github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||||
github.com/goodsign/monday v1.0.0 h1:Yyk/s/WgudMbAJN6UWSU5xAs8jtNewfqtVblAlw0yoc=
|
github.com/goodsign/monday v1.0.0 h1:Yyk/s/WgudMbAJN6UWSU5xAs8jtNewfqtVblAlw0yoc=
|
||||||
github.com/goodsign/monday v1.0.0/go.mod h1:r4T4breXpoFwspQNM+u2sLxJb2zyTaxVGqUfTBjWOu8=
|
github.com/goodsign/monday v1.0.0/go.mod h1:r4T4breXpoFwspQNM+u2sLxJb2zyTaxVGqUfTBjWOu8=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gotk3/gotk3 v0.4.0 h1:TIuhyQitGeRTxOQIV3AJlYtEWWJpC74JHwAIsxlH8MU=
|
|
||||||
github.com/gotk3/gotk3 v0.4.0/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo=
|
|
||||||
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194 h1:bB6XWpxMt2isCWqzjXN8tfVazjxvD8nRJrNoKcL0xAc=
|
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194 h1:bB6XWpxMt2isCWqzjXN8tfVazjxvD8nRJrNoKcL0xAc=
|
||||||
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
@ -34,5 +38,7 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
|
@ -48,12 +48,13 @@ type WindowHeaderer interface {
|
||||||
func Main(wfn func() WindowHeaderer) {
|
func Main(wfn func() WindowHeaderer) {
|
||||||
App.Application.Connect("activate", func() {
|
App.Application.Connect("activate", func() {
|
||||||
App.Header, _ = gtk.HeaderBarNew()
|
App.Header, _ = gtk.HeaderBarNew()
|
||||||
App.Header.Show()
|
|
||||||
App.Header.SetShowCloseButton(true)
|
App.Header.SetShowCloseButton(true)
|
||||||
|
App.Header.Show()
|
||||||
|
|
||||||
App.Window, _ = gtk.ApplicationWindowNew(App.Application)
|
App.Window, _ = gtk.ApplicationWindowNew(App.Application)
|
||||||
App.Window.Show()
|
App.Window.SetDefaultSize(750, 400)
|
||||||
App.Window.SetTitlebar(App.Header)
|
App.Window.SetTitlebar(App.Header)
|
||||||
|
App.Window.Show()
|
||||||
|
|
||||||
// Execute the function later, because we need it to run after
|
// Execute the function later, because we need it to run after
|
||||||
// initialization.
|
// initialization.
|
||||||
|
|
|
@ -8,23 +8,31 @@ import (
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
*gtk.ScrolledWindow
|
*gtk.ScrolledWindow
|
||||||
main *gtk.Box
|
main *gtk.Grid
|
||||||
messages map[string]Message
|
messages map[string]Message
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainer() *Container {
|
func NewContainer() *Container {
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 3)
|
grid, _ := gtk.GridNew()
|
||||||
box.Show()
|
grid.SetColumnSpacing(8)
|
||||||
|
grid.SetRowSpacing(5)
|
||||||
|
grid.SetMarginStart(5)
|
||||||
|
grid.SetMarginEnd(5)
|
||||||
|
grid.Show()
|
||||||
|
|
||||||
sw, _ := gtk.ScrolledWindowNew(nil, nil)
|
sw, _ := gtk.ScrolledWindowNew(nil, nil)
|
||||||
|
sw.Add(grid)
|
||||||
|
sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
||||||
sw.Show()
|
sw.Show()
|
||||||
|
|
||||||
return &Container{sw, box, map[string]Message{}}
|
return &Container{sw, grid, map[string]Message{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Reset() {
|
func (c *Container) Reset() {
|
||||||
for _, msg := range c.messages {
|
// does this actually work?
|
||||||
c.main.Remove(msg)
|
var rows = len(c.messages)
|
||||||
|
for i := 0; i < rows; i++ {
|
||||||
|
c.main.RemoveRow(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.messages = nil
|
c.messages = nil
|
||||||
|
@ -32,9 +40,11 @@ func (c *Container) Reset() {
|
||||||
|
|
||||||
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||||
gts.ExecAsync(func() {
|
gts.ExecAsync(func() {
|
||||||
var msgc = NewMessage(msg)
|
msgc := NewMessage(msg)
|
||||||
|
msgc.index = len(c.messages) // unsure
|
||||||
|
|
||||||
c.messages[msgc.ID] = msgc
|
c.messages[msgc.ID] = msgc
|
||||||
c.main.Add(msgc)
|
msgc.Attach(c.main, msgc.index)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +68,7 @@ func (c *Container) DeleteMessage(msg cchat.MessageDelete) {
|
||||||
gts.ExecAsync(func() {
|
gts.ExecAsync(func() {
|
||||||
if m, ok := c.messages[msg.ID()]; ok {
|
if m, ok := c.messages[msg.ID()]; ok {
|
||||||
delete(c.messages, msg.ID())
|
delete(c.messages, msg.ID())
|
||||||
c.main.Remove(m)
|
c.main.RemoveRow(m.index)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,38 +11,40 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
index int
|
||||||
ID string
|
ID string
|
||||||
Nonce string
|
Nonce string
|
||||||
|
|
||||||
*gtk.Box
|
|
||||||
Timestamp *gtk.Label
|
Timestamp *gtk.Label
|
||||||
Username *gtk.Label
|
Username *gtk.Label
|
||||||
Content *gtk.Label
|
Content *gtk.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessage(msg cchat.MessageCreate) Message {
|
func NewMessage(msg cchat.MessageCreate) Message {
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 3)
|
|
||||||
box.Show()
|
|
||||||
|
|
||||||
ts, _ := gtk.LabelNew("")
|
ts, _ := gtk.LabelNew("")
|
||||||
ts.Show()
|
ts.SetLineWrap(true)
|
||||||
ts.SetWidthChars(12)
|
|
||||||
ts.SetLineWrapMode(pango.WRAP_WORD)
|
ts.SetLineWrapMode(pango.WRAP_WORD)
|
||||||
|
ts.SetHAlign(gtk.ALIGN_END)
|
||||||
|
ts.SetVAlign(gtk.ALIGN_START)
|
||||||
|
ts.Show()
|
||||||
|
|
||||||
user, _ := gtk.LabelNew("")
|
user, _ := gtk.LabelNew("")
|
||||||
user.Show()
|
user.SetLineWrap(true)
|
||||||
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
||||||
|
user.SetHAlign(gtk.ALIGN_END)
|
||||||
|
user.SetVAlign(gtk.ALIGN_START)
|
||||||
|
user.Show()
|
||||||
|
|
||||||
content, _ := gtk.LabelNew("")
|
content, _ := gtk.LabelNew("")
|
||||||
|
content.SetHExpand(true)
|
||||||
|
content.SetXAlign(0) // left-align with size filled
|
||||||
|
content.SetVAlign(gtk.ALIGN_START)
|
||||||
|
content.SetLineWrap(true)
|
||||||
|
content.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
||||||
content.Show()
|
content.Show()
|
||||||
|
|
||||||
box.PackStart(ts, false, false, 0)
|
|
||||||
box.PackStart(user, false, false, 0)
|
|
||||||
box.PackStart(content, true, true, 0)
|
|
||||||
|
|
||||||
m := Message{
|
m := Message{
|
||||||
ID: msg.ID(),
|
ID: msg.ID(),
|
||||||
Box: box,
|
|
||||||
Timestamp: ts,
|
Timestamp: ts,
|
||||||
Username: user,
|
Username: user,
|
||||||
Content: content,
|
Content: content,
|
||||||
|
@ -58,6 +60,12 @@ func NewMessage(msg cchat.MessageCreate) Message {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Message) Attach(grid *gtk.Grid, row int) {
|
||||||
|
grid.Attach(m.Timestamp, 0, row, 1, 1)
|
||||||
|
grid.Attach(m.Username, 1, row, 1, 1)
|
||||||
|
grid.Attach(m.Content, 2, row, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Message) UpdateTimestamp(t time.Time) {
|
func (m *Message) UpdateTimestamp(t time.Time) {
|
||||||
m.Timestamp.SetLabel(humanize.TimeAgo(t))
|
m.Timestamp.SetLabel(humanize.TimeAgo(t))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ func NewView() *View {
|
||||||
sendinput := input.NewField()
|
sendinput := input.NewField()
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
box.Show()
|
|
||||||
box.PackStart(container, true, true, 0)
|
box.PackStart(container, true, true, 0)
|
||||||
box.PackStart(sendinput, false, false, 0)
|
box.PackStart(sendinput, false, false, 0)
|
||||||
|
box.Show()
|
||||||
|
|
||||||
return &View{
|
return &View{
|
||||||
Box: box,
|
Box: box,
|
||||||
|
@ -37,6 +37,9 @@ func (v *View) JoinServer(server cchat.ServerMessage) {
|
||||||
if err := v.current.LeaveServer(); err != nil {
|
if err := v.current.LeaveServer(); err != nil {
|
||||||
log.Error(errors.Wrap(err, "Error leaving server"))
|
log.Error(errors.Wrap(err, "Error leaving server"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean all messages.
|
||||||
|
v.Container.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
v.current = server
|
v.current = server
|
||||||
|
|
25
internal/ui/primitives/primitives.go
Normal file
25
internal/ui/primitives/primitives.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package primitives
|
||||||
|
|
||||||
|
import "github.com/gotk3/gotk3/gtk"
|
||||||
|
|
||||||
|
type StyleContexter interface {
|
||||||
|
GetStyleContext() (*gtk.StyleContext, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddClass(styleCtx StyleContexter, classes ...string) {
|
||||||
|
var style, _ = styleCtx.GetStyleContext()
|
||||||
|
for _, class := range classes {
|
||||||
|
style.AddClass(class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bin interface {
|
||||||
|
GetChild() (gtk.IWidget, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Bin = (*gtk.Bin)(nil)
|
||||||
|
|
||||||
|
func BinLeftAlignLabel(bin Bin) {
|
||||||
|
widget, _ := bin.GetChild()
|
||||||
|
widget.(interface{ SetXAlign(float64) }).SetXAlign(0)
|
||||||
|
}
|
|
@ -2,8 +2,10 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
"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/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +19,12 @@ func NewView() *View {
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
box.Show()
|
box.Show()
|
||||||
|
|
||||||
|
primitives.AddClass(box, "services")
|
||||||
|
|
||||||
sw, _ := gtk.ScrolledWindowNew(nil, nil)
|
sw, _ := gtk.ScrolledWindowNew(nil, nil)
|
||||||
sw.Show()
|
sw.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||||
sw.Add(box)
|
sw.Add(box)
|
||||||
|
sw.Show()
|
||||||
|
|
||||||
return &View{
|
return &View{
|
||||||
sw,
|
sw,
|
||||||
|
@ -28,8 +33,8 @@ func NewView() *View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *View) AddService(svc cchat.Service) {
|
func (v *View) AddService(svc cchat.Service, rowctrl server.RowController) {
|
||||||
s := NewContainer(svc)
|
s := NewContainer(svc, rowctrl)
|
||||||
v.Services = append(v.Services, s)
|
v.Services = append(v.Services, s)
|
||||||
v.Box.Add(s)
|
v.Box.Add(s)
|
||||||
}
|
}
|
||||||
|
@ -39,24 +44,36 @@ type Container struct {
|
||||||
header *header
|
header *header
|
||||||
revealer *gtk.Revealer
|
revealer *gtk.Revealer
|
||||||
children *children
|
children *children
|
||||||
|
rowctrl server.RowController
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainer(svc cchat.Service) *Container {
|
func NewContainer(svc cchat.Service, rowctrl server.RowController) *Container {
|
||||||
header := newHeader(svc)
|
header := newHeader(svc)
|
||||||
|
|
||||||
children := newChildren()
|
children := newChildren()
|
||||||
|
|
||||||
chrev, _ := gtk.RevealerNew()
|
chrev, _ := gtk.RevealerNew()
|
||||||
chrev.Show()
|
|
||||||
chrev.SetRevealChild(false)
|
chrev.SetRevealChild(false)
|
||||||
chrev.Add(children)
|
chrev.Add(children)
|
||||||
|
chrev.Show()
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
box.Show()
|
box.Show()
|
||||||
box.PackStart(header, false, false, 0)
|
box.PackStart(header, false, false, 0)
|
||||||
box.PackStart(chrev, false, false, 0)
|
box.PackStart(chrev, false, false, 0)
|
||||||
|
|
||||||
var container = &Container{box, header, chrev, children}
|
primitives.AddClass(box, "service")
|
||||||
|
|
||||||
|
var container = &Container{box, header, chrev, children, rowctrl}
|
||||||
|
|
||||||
|
// On click, toggle reveal.
|
||||||
|
header.reveal.Connect("clicked", func() {
|
||||||
|
revealed := !chrev.GetRevealChild()
|
||||||
|
chrev.SetRevealChild(revealed)
|
||||||
|
header.reveal.SetActive(revealed)
|
||||||
|
})
|
||||||
|
|
||||||
|
// On click, show the auth dialog.
|
||||||
header.add.Connect("clicked", func() {
|
header.add.Connect("clicked", func() {
|
||||||
auth.NewDialog(svc.Name(), svc.Authenticate(), container.addSession)
|
auth.NewDialog(svc.Name(), svc.Authenticate(), container.addSession)
|
||||||
})
|
})
|
||||||
|
@ -65,31 +82,34 @@ func NewContainer(svc cchat.Service) *Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) addSession(ses cchat.Session) {
|
func (c *Container) addSession(ses cchat.Session) {
|
||||||
srow := session.New(ses)
|
srow := session.New(ses, c.rowctrl)
|
||||||
c.children.addSessionRow(srow)
|
c.children.addSessionRow(srow)
|
||||||
}
|
}
|
||||||
|
|
||||||
type header struct {
|
type header struct {
|
||||||
*gtk.Box
|
*gtk.Box
|
||||||
label *gtk.Label
|
reveal *gtk.ToggleButton
|
||||||
add *gtk.Button
|
add *gtk.Button
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHeader(svc cchat.Service) *header {
|
func newHeader(svc cchat.Service) *header {
|
||||||
label, _ := gtk.LabelNew(svc.Name())
|
reveal, _ := gtk.ToggleButtonNewWithLabel(svc.Name())
|
||||||
label.Show()
|
primitives.BinLeftAlignLabel(reveal) // do this first
|
||||||
label.SetXAlign(0)
|
|
||||||
|
reveal.SetRelief(gtk.RELIEF_NONE)
|
||||||
|
reveal.SetMode(true)
|
||||||
|
reveal.Show()
|
||||||
|
|
||||||
add, _ := gtk.ButtonNewFromIconName("list-add-symbolic", gtk.ICON_SIZE_BUTTON)
|
add, _ := gtk.ButtonNewFromIconName("list-add-symbolic", gtk.ICON_SIZE_BUTTON)
|
||||||
add.SetRelief(gtk.RELIEF_NONE)
|
add.SetRelief(gtk.RELIEF_NONE)
|
||||||
add.Show()
|
add.Show()
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
box.Show()
|
box.PackStart(reveal, true, true, 0)
|
||||||
box.PackStart(label, true, true, 5)
|
|
||||||
box.PackStart(add, false, false, 0)
|
box.PackStart(add, false, false, 0)
|
||||||
|
box.Show()
|
||||||
|
|
||||||
return &header{box, label, add}
|
return &header{box, reveal, add}
|
||||||
}
|
}
|
||||||
|
|
||||||
type children struct {
|
type children struct {
|
||||||
|
|
|
@ -4,37 +4,49 @@ import (
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ChildrenMargin = 24
|
||||||
|
|
||||||
|
type RowController interface {
|
||||||
|
MessageRowSelected(*Row, cchat.ServerMessage)
|
||||||
|
}
|
||||||
|
|
||||||
type Row struct {
|
type Row struct {
|
||||||
*gtk.Box
|
*gtk.Box
|
||||||
Button *gtk.Button
|
Button *gtk.Button
|
||||||
Server cchat.Server
|
Server cchat.Server
|
||||||
|
|
||||||
|
ctrl RowController
|
||||||
|
|
||||||
// enum 1
|
// enum 1
|
||||||
clicked func(*Row)
|
|
||||||
message cchat.ServerMessage
|
message cchat.ServerMessage
|
||||||
|
|
||||||
// enum 2
|
// enum 2
|
||||||
children *Children
|
children *Children
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(server cchat.Server) *Row {
|
func New(server cchat.Server, ctrl RowController) *Row {
|
||||||
name, err := server.Name()
|
name, err := server.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(errors.Wrap(err, "Failed to get the server name"))
|
log.Error(errors.Wrap(err, "Failed to get the server name"))
|
||||||
name = "no name"
|
name = "no name"
|
||||||
}
|
}
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
button, _ := gtk.ButtonNewWithLabel(name)
|
||||||
|
primitives.BinLeftAlignLabel(button)
|
||||||
|
|
||||||
|
button.SetRelief(gtk.RELIEF_NONE)
|
||||||
|
button.Show()
|
||||||
|
|
||||||
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
|
box.PackStart(button, false, false, 0)
|
||||||
box.Show()
|
box.Show()
|
||||||
|
|
||||||
button, _ := gtk.ButtonNew()
|
primitives.AddClass(box, "server")
|
||||||
button.Show()
|
|
||||||
button.SetRelief(gtk.RELIEF_NONE)
|
|
||||||
button.SetLabel(name)
|
|
||||||
|
|
||||||
// TODO: images
|
// TODO: images
|
||||||
|
|
||||||
|
@ -42,31 +54,30 @@ func New(server cchat.Server) *Row {
|
||||||
Box: box,
|
Box: box,
|
||||||
Button: button,
|
Button: button,
|
||||||
Server: server,
|
Server: server,
|
||||||
|
ctrl: ctrl,
|
||||||
}
|
}
|
||||||
button.Connect("clicked", row.onClick)
|
button.Connect("clicked", row.onClick)
|
||||||
|
|
||||||
switch server := server.(type) {
|
switch server := server.(type) {
|
||||||
case cchat.ServerList:
|
case cchat.ServerList:
|
||||||
row.children = NewChildren(server)
|
row.children = NewChildren(server, ctrl)
|
||||||
|
box.PackStart(row.children, false, false, 0)
|
||||||
|
|
||||||
|
primitives.AddClass(box, "server-list")
|
||||||
|
|
||||||
case cchat.ServerMessage:
|
case cchat.ServerMessage:
|
||||||
row.message = server
|
row.message = server
|
||||||
|
|
||||||
|
primitives.AddClass(box, "server-message")
|
||||||
}
|
}
|
||||||
|
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOnClick sets the callback when the server is clicked. This only works if
|
|
||||||
// the passed in server implements ServerMessage.
|
|
||||||
func (row *Row) SetOnClick(clicked func(*Row)) {
|
|
||||||
if row.message != nil {
|
|
||||||
row.clicked = clicked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (row *Row) onClick() {
|
func (row *Row) onClick() {
|
||||||
switch {
|
switch {
|
||||||
case row.message != nil:
|
case row.message != nil:
|
||||||
row.clicked(row)
|
row.ctrl.MessageRowSelected(row, row.message)
|
||||||
case row.children != nil:
|
case row.children != nil:
|
||||||
row.children.SetRevealChild(!row.children.GetRevealChild())
|
row.children.SetRevealChild(!row.children.GetRevealChild())
|
||||||
}
|
}
|
||||||
|
@ -75,22 +86,27 @@ func (row *Row) onClick() {
|
||||||
type Children struct {
|
type Children struct {
|
||||||
*gtk.Revealer
|
*gtk.Revealer
|
||||||
Main *gtk.Box
|
Main *gtk.Box
|
||||||
Rows []*Row
|
|
||||||
List cchat.ServerList
|
List cchat.ServerList
|
||||||
|
|
||||||
|
Rows []*Row
|
||||||
|
rowctrl RowController
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChildren(list cchat.ServerList) *Children {
|
func NewChildren(list cchat.ServerList, ctrl RowController) *Children {
|
||||||
rev, _ := gtk.RevealerNew()
|
main, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
rev.Show()
|
main.SetMarginStart(ChildrenMargin)
|
||||||
rev.SetRevealChild(false)
|
|
||||||
|
|
||||||
main, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
|
||||||
main.Show()
|
main.Show()
|
||||||
|
|
||||||
|
rev, _ := gtk.RevealerNew()
|
||||||
|
rev.SetRevealChild(false)
|
||||||
|
rev.Add(main)
|
||||||
|
rev.Show()
|
||||||
|
|
||||||
children := &Children{
|
children := &Children{
|
||||||
Revealer: rev,
|
Revealer: rev,
|
||||||
Main: main,
|
Main: main,
|
||||||
List: list,
|
List: list,
|
||||||
|
rowctrl: ctrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := list.Servers(children); err != nil {
|
if err := list.Servers(children); err != nil {
|
||||||
|
@ -109,7 +125,7 @@ func (c *Children) SetServers(servers []cchat.Server) {
|
||||||
c.Rows = make([]*Row, len(servers))
|
c.Rows = make([]*Row, len(servers))
|
||||||
|
|
||||||
for i, server := range servers {
|
for i, server := range servers {
|
||||||
row := New(server)
|
row := New(server, c.rowctrl)
|
||||||
c.Rows[i] = row
|
c.Rows[i] = row
|
||||||
c.Main.Add(row)
|
c.Main.Add(row)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package session
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/log"
|
"github.com/diamondburned/cchat-gtk/internal/log"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -10,35 +11,46 @@ import (
|
||||||
|
|
||||||
type Row struct {
|
type Row struct {
|
||||||
*gtk.Box
|
*gtk.Box
|
||||||
Button *gtk.Button
|
Button *gtk.ToggleButton
|
||||||
Session cchat.Session
|
Session cchat.Session
|
||||||
|
|
||||||
Servers *server.Children
|
Servers *server.Children
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ses cchat.Session) *Row {
|
func New(ses cchat.Session, rowctrl server.RowController) *Row {
|
||||||
n, err := ses.Name()
|
n, err := ses.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(errors.Wrap(err, "Failed to get the username"))
|
log.Error(errors.Wrap(err, "Failed to get the username"))
|
||||||
n = "no name"
|
n = "no name"
|
||||||
}
|
}
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
button, _ := gtk.ToggleButtonNewWithLabel(n)
|
||||||
box.Show()
|
primitives.BinLeftAlignLabel(button)
|
||||||
|
|
||||||
button, _ := gtk.ButtonNew()
|
|
||||||
button.Show()
|
|
||||||
button.SetRelief(gtk.RELIEF_NONE)
|
button.SetRelief(gtk.RELIEF_NONE)
|
||||||
button.SetLabel(n)
|
button.Show()
|
||||||
|
|
||||||
rev, _ := gtk.RevealerNew()
|
servers := server.NewChildren(ses, rowctrl)
|
||||||
rev.Show()
|
|
||||||
rev.SetRevealChild(false)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
|
box.Show()
|
||||||
|
box.SetMarginStart(server.ChildrenMargin)
|
||||||
|
box.PackStart(button, false, false, 0)
|
||||||
|
box.PackStart(servers, false, false, 0)
|
||||||
|
|
||||||
|
primitives.AddClass(box, "session")
|
||||||
|
|
||||||
|
// On click, toggle reveal.
|
||||||
|
button.Connect("clicked", func() {
|
||||||
|
revealed := !servers.GetRevealChild()
|
||||||
|
servers.SetRevealChild(revealed)
|
||||||
|
button.SetActive(revealed)
|
||||||
|
})
|
||||||
|
|
||||||
return &Row{
|
return &Row{
|
||||||
Box: box,
|
Box: box,
|
||||||
Button: button,
|
Button: button,
|
||||||
Session: ses,
|
Session: ses,
|
||||||
Servers: server.NewChildren(ses),
|
Servers: servers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ui
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||||
|
"github.com/diamondburned/cchat-gtk/internal/ui/service/session/server"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,8 +15,9 @@ type Application struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ gts.Windower = (*Application)(nil)
|
_ gts.Windower = (*Application)(nil)
|
||||||
_ gts.Headerer = (*Application)(nil)
|
_ gts.Headerer = (*Application)(nil)
|
||||||
|
_ server.RowController = (*Application)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApplication() *Application {
|
func NewApplication() *Application {
|
||||||
|
@ -28,7 +30,11 @@ func NewApplication() *Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) AddService(svc cchat.Service) {
|
func (app *Application) AddService(svc cchat.Service) {
|
||||||
app.window.Services.AddService(svc)
|
app.window.Services.AddService(svc, app)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) MessageRowSelected(_ *server.Row, smsg cchat.ServerMessage) {
|
||||||
|
app.window.MessageView.JoinServer(smsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) Header() gtk.IWidget {
|
func (app *Application) Header() gtk.IWidget {
|
||||||
|
|
|
@ -17,10 +17,14 @@ func newWindow() *window {
|
||||||
services.SetSizeRequest(LeftWidth, -1)
|
services.SetSizeRequest(LeftWidth, -1)
|
||||||
mesgview := message.NewView()
|
mesgview := message.NewView()
|
||||||
|
|
||||||
|
separator, _ := gtk.SeparatorNew(gtk.ORIENTATION_VERTICAL)
|
||||||
|
separator.Show()
|
||||||
|
|
||||||
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||||
box.Show()
|
|
||||||
box.PackStart(services, false, false, 0)
|
box.PackStart(services, false, false, 0)
|
||||||
|
box.PackStart(separator, false, false, 0)
|
||||||
box.PackStart(mesgview, true, true, 0)
|
box.PackStart(mesgview, true, true, 0)
|
||||||
|
box.Show()
|
||||||
|
|
||||||
return &window{box, services, mesgview}
|
return &window{box, services, mesgview}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue