Skip to content

Instantly share code, notes, and snippets.

@diamondburned
Last active April 4, 2020 01:37
Show Gist options
  • Save diamondburned/bace77818568acfe5b5bd64619bea3da to your computer and use it in GitHub Desktop.
Save diamondburned/bace77818568acfe5b5bd64619bea3da to your computer and use it in GitHub Desktop.
A second attempt at a better Gtk API for Golang.
package main
type TodoItem struct {
gtk.ListBoxRow
Entry gtk.Entry // acts as label
Remove gtk.Button
item string
// local, doesn't have to do with Gtk
todo *Todo
}
var _ Initializer = (*TodoItem)(nil)
func NewTodoItem(t *Todo, item string) {
return &TodoItem{
todo: t,
item: item,
}
}
func (t *TodoItem) Initialize() error {
t.ListBoxRow = gtk.ListBoxRowNew()
// SetKey allows the parent ListBox to easily differ between old and new
// nodes. ReactJS does this:
// https://reactjs.org/docs/reconciliation.html#keys
t.ListBoxRow.SetKey(t.item)
t.Entry = gtk.EntryNew()
t.Entry.SetMargin(8)
t.Entry.Bind(&t.item)
t.Entry.Connect("changed", func(content string) {
// Manually update the key as well. Can't really think of a better way
// other than to use a Serial integer.
// Content is just a copy of t.item's value.
t.ListBoxRow.SetKey(content)
// There's no need to update again here, as we're not modifying t.item.
})
t.Remove = gtk.ButtonNew()
t.Remove.Connect("clicked", t.RemoveItem) // Bind(func())
// Add containers into ListBoxRow:
t.Add(t.Entry)
t.Add(t.Remove)
t.ShowAll()
return nil
}
func (t *TodoItem) RemoveItem() {
t.todo.remove(t.item)
// Update, which updates the list as well.
t.todo.Update()
}
package main
type Initializer interface {
Initialize() error // error is only in case, gets floated up.
}
func main() {
a := gtk.ApplicationNew("com.github.diamondburned.demo")
w := gtk.ApplicationWindowNew(a)
// The magic happens.
w.Add(&Todo{})
w.ShowAll()
gtk.Main()
}
package main
type Todo struct {
gtk.Box
Entry gtk.Entry
Add gtk.Button
// local states
ItemContainer gtk.ListBox
Items []*TodoItem
// more local states
entryText string
}
var _ Initializer = (*Todo)(nil)
func (t *Todo) Initialize() error {
t.Box = gtk.BoxNew()
t.Entry = gtk.EntryNew()
t.Entry.SetMargin(10)
t.Entry.SetMarginRight(5)
// Bind changes the string's content with each edit.
t.Entry.Bind(&t.entryText)
t.Add = gtk.ButtonNew()
t.Add.Connect("clicked", func() {
t.add(t.entryText)
t.entryText = ""
// Manually update the main node. This triggers ListBox's internal
// pointer to the slice as well, which updates the list of Todo items.
// It will also trigger Entry and edit its content to nothing.
t.Update()
})
t.ItemContainer = gtk.ListBoxNew()
t.ItemContainer.SetMargin(10)
t.ItemContainer.Bind(&t.Items) // allow (*gtk.ListBox).SetListCallback()
// This is needed to be called normally in Gtk. This draft adds an extra
// functionality of first-time calling mounted methods to populate the
// state.
t.ShowAll()
return nil
}
func (t *Todo) add(item string) {
t.Items = append(t.Items, NewTodoItem(t, item))
}
// remove is called by one of the Todos.
func (t *Todo) remove(item string) {
for i, todo := range t.Items {
if todo.item == item {
t.Items = append(t.Items[:i], t.Items[i+1:]...)
return
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment