]> git.openfl.eu Git - chat-scroll.git/commitdiff
multiwindow setup main
authorFl_GUI <flor.guilini@hotmail.com>
Sat, 28 Dec 2024 15:49:51 +0000 (16:49 +0100)
committerFl_GUI <flor.guilini@hotmail.com>
Sat, 28 Dec 2024 15:49:51 +0000 (16:49 +0100)
17 files changed:
chat/create.go
fynepatch/keyvaluelayout.go [new file with mode: 0644]
main.go
model/model.go
model/tokenState.go [new file with mode: 0644]
model/window.go [new file with mode: 0644]
persistence/file.go [new file with mode: 0644]
persistence/io.go [new file with mode: 0644]
persistence/types.go [new file with mode: 0644]
status/create.go
status/twitchconn/twitchConnection.go
todo
token/authenticate.go [new file with mode: 0644]
token/authhandler.go [new file with mode: 0644]
token/page.html [new file with mode: 0644]
windowconfig/windowconfig.go [new file with mode: 0644]
windowconfig/windowconfigitem.go [new file with mode: 0644]

index 814013a56ac60dd5c8e4f4dd30e807a13e6fc0eb..c1d2424855d4aa671cd941cfc3be7d32f201a0fb 100644 (file)
@@ -1,6 +1,7 @@
 package chat
 
 import (
+       "fmt"
        "sync"
 
        chatMessages "fl-gui.name/twitchchat/twitch/core/messages"
@@ -8,10 +9,13 @@ import (
        "fyne.io/fyne/v2"
        "fyne.io/fyne/v2/theme"
        "go.openfl.eu/chat-scroller/messages"
+       "go.openfl.eu/chat-scroller/model"
 )
 
 type ChatWindow struct {
        content      *scrollCanvas
+       window       fyne.Window
+       windowModel  *model.Window
        messagesSync sync.Mutex
        textSize     float32
 }
@@ -29,16 +33,33 @@ func (c *ChatWindow) OnMessage(m chatMessages.Message) {
        c.content.Add(co)
 }
 
-func NewChat(app fyne.App) fyne.Window {
-       w := app.NewWindow("chat")
+func (c *ChatWindow) DataChanged() {
+       name, err := c.windowModel.Name.Get()
+       if err != nil {
+               fmt.Printf("Could not set chat name: %s\n", err)
+       } else {
+               c.window.SetTitle(name)
+       }
+}
+
+func NewChat(app fyne.App, window *model.Window) fyne.Window {
+       windowName, err := window.Name.Get()
+       if err != nil {
+               fmt.Printf("Could not open new window: %s\n", err)
+               return nil
+       }
+       w := app.NewWindow(windowName)
        content := newScrollingCanvas()
 
-       cw := ChatWindow{content, sync.Mutex{}, app.Settings().Theme().Size(theme.SizeNameText)}
+       cw := ChatWindow{content, w, window, sync.Mutex{}, app.Settings().Theme().Size(theme.SizeNameText)}
+
        messages.Listen(&cw)
+       window.Name.AddListener(&cw)
 
        w.SetContent(content)
        w.SetOnClosed(func() {
                messages.StopListening(&cw)
+               window.Name.RemoveListener(&cw)
        })
 
        return w
diff --git a/fynepatch/keyvaluelayout.go b/fynepatch/keyvaluelayout.go
new file mode 100644 (file)
index 0000000..d2a042c
--- /dev/null
@@ -0,0 +1,66 @@
+package fynepatch
+
+import "fyne.io/fyne/v2"
+
+type KeyValueLayout struct {
+}
+
+// Places children in a two column table with
+// row major order. The first column will
+// be as wide as the widest child in that column,
+// the second will fill the remaining space.
+func NewKeyValueLayout() fyne.Layout {
+       return &KeyValueLayout{}
+}
+
+func (kv *KeyValueLayout) Layout(objects []fyne.CanvasObject, totalSize fyne.Size) {
+       var keyWidth float32 = 0
+       for i := 0; i < len(objects); i += 2 {
+               kWidth := objects[i].MinSize().Width
+               if kWidth > keyWidth {
+                       keyWidth = kWidth
+               }
+       }
+
+       var h float32 = 0
+       var k fyne.CanvasObject
+       for i := 0; i < len(objects); i += 2 {
+               k = objects[i]
+               // row height
+               dh := k.MinSize().Height
+               if i+1 < len(objects) {
+                       valueHeight := objects[i+1].MinSize().Height
+                       if valueHeight > dh {
+                               dh = valueHeight
+                       }
+               }
+               // place key
+               k.Resize(fyne.Size{keyWidth, dh})
+               k.Move(fyne.Position{0, h})
+
+               // place value
+               if i+1 < len(objects) {
+                       value := objects[i+1]
+                       value.Resize(fyne.Size{totalSize.Width - keyWidth, dh})
+                       value.Move(fyne.Position{keyWidth, h})
+               }
+               // calculate height offset
+               h += dh
+       }
+}
+
+func (kv *KeyValueLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
+       minsize := fyne.Size{0, 0}
+       for i := 0; i < len(objects); i += 2 {
+               key := objects[i]
+               rowSize := key.MinSize()
+               if i+1 < len(objects) {
+                       valueHeight := objects[i+1].MinSize().Height
+                       if valueHeight > rowSize.Height {
+                               rowSize.Height = valueHeight
+                       }
+               }
+               minsize = minsize.Add(rowSize)
+       }
+       return minsize
+}
diff --git a/main.go b/main.go
index e1a0ef9681e193cf10f13758d263afc4e64bcc71..d8642c4c8ef5ff168973cfb9d81e500a3e5d59a4 100644 (file)
--- a/main.go
+++ b/main.go
@@ -8,11 +8,13 @@ import (
 
        "fyne.io/fyne/v2"
        "fyne.io/fyne/v2/app"
+       "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/data/binding"
        "fyne.io/fyne/v2/theme"
        "go.openfl.eu/chat-scroller/model"
        "go.openfl.eu/chat-scroller/status"
        "go.openfl.eu/chat-scroller/token"
+       "go.openfl.eu/chat-scroller/windowconfig"
 
        // for init purposes
        _ "go.openfl.eu/chat-scroller/decorations"
@@ -56,7 +58,14 @@ func main() {
 
        scroller.Settings().SetTheme(chattheme{theme.LightTheme()})
 
-       go status.CreateSetupWindow(scroller)
+       statusWindow := scroller.NewWindow("Fl_GUI's chat scroller")
+       statusWindow.SetMaster()
+
+       statusWindow.Show()
+       statusWindow.SetContent(container.NewVBox(
+               status.CreateSetupWindow(scroller),
+               windowconfig.CreateWindowConfig(scroller),
+       ))
 
        scroller.Run()
 }
index 519d7c084e62c10b99c628e4482e0dbc46ad557e..181d363138c5f0ed4f63ec8c5af115c06f14bc4f 100644 (file)
@@ -7,7 +7,8 @@ import (
 )
 
 type Model struct {
-       Twitch TwitchModel
+       Twitch  TwitchModel
+       windows map[*Window]struct{}
 }
 
 type TwitchModel struct {
@@ -17,6 +18,16 @@ type TwitchModel struct {
 
 var Application Model
 
+func (a Model) AddWindow() *Window {
+       w := newWindow()
+       a.windows[&w] = struct{}{}
+       return &w
+}
+
+func (a Model) RemoveWindow(w *Window) {
+       delete(a.windows, w)
+}
+
 func init() {
        {
                tw := &Application.Twitch
@@ -25,6 +36,11 @@ func init() {
                tw.Token = binding.NewString()
        }
 
+       {
+               windows := make(map[*Window]struct{})
+               Application.windows = windows
+       }
+
        debug := true
        if debug {
                Application.Twitch.TokenState.AddListener(binding.NewDataListener(func() {
diff --git a/model/tokenState.go b/model/tokenState.go
new file mode 100644 (file)
index 0000000..d7c9439
--- /dev/null
@@ -0,0 +1,9 @@
+package model
+
+type TokenState int
+
+const (
+       Valid TokenState = iota
+       Invalid
+       Absent
+)
diff --git a/model/window.go b/model/window.go
new file mode 100644 (file)
index 0000000..b2a8d09
--- /dev/null
@@ -0,0 +1,52 @@
+package model
+
+import (
+       "image/color"
+
+       "fyne.io/fyne/v2/data/binding"
+)
+
+// Window is an Untyped containing a *Window
+type Window struct {
+       Name          binding.String
+       Channel       binding.String
+       FontSize      binding.Float
+       Width, Height binding.Int
+       Palette       binding.Untyped // color.Palette
+}
+
+func newWindow() Window {
+       w := Window{
+               binding.NewString(),
+               binding.NewString(),
+               binding.NewFloat(),
+               binding.NewInt(), binding.NewInt(),
+               binding.NewUntyped(),
+       }
+       w.Name.Set("chat")
+       w.Channel.Set("")
+       w.FontSize.Set(20)
+       w.Width.Set(200)
+       w.Height.Set(200)
+       w.Palette.Set(make([]color.Color, 0))
+       return w
+}
+
+func (w *Window) GetPalette() (color.Palette, error) {
+       a, err := w.Palette.Get()
+       if err != nil {
+               return nil, err
+       }
+       return a.(color.Palette), nil
+}
+
+func (w *Window) AddPaletteColor(col color.Color) error {
+       p, err := w.GetPalette()
+       if err != nil {
+               return err
+       }
+       p = append(p, col)
+       return w.Palette.Set(p)
+}
+
+func (w *Window) ClearPalette() { w.Palette.Set(make([]color.Color, 0)) }
diff --git a/persistence/file.go b/persistence/file.go
new file mode 100644 (file)
index 0000000..d667a34
--- /dev/null
@@ -0,0 +1,21 @@
+package persistence
+
+import (
+       "os"
+       "path"
+)
+
+var filepath string
+
+func getFilePath() string {
+       if filepath == "" {
+               configDir, err := os.UserConfigDir()
+               if err != nil {
+                       panic(err)
+               }
+
+               filepath = path.Join(configDir, "chat-scroller.config.json")
+       }
+
+       return filepath
+}
diff --git a/persistence/io.go b/persistence/io.go
new file mode 100644 (file)
index 0000000..2af37ae
--- /dev/null
@@ -0,0 +1,38 @@
+package persistence
+
+import (
+       "encoding/json"
+       "fmt"
+       "os"
+
+       "go.openfl.eu/chat-scroller/model"
+)
+
+func must[T any](v T, err error) T {
+       return v
+}
+
+func Write(m *model.Model) error {
+       _ = PersistedStorage{
+               Windows{},
+       }
+       return nil
+}
+
+func Read() PersistedStorage {
+       p := getFilePath()
+       f, err := os.Open(p)
+       if err != nil {
+               fmt.Println(err)
+               return PersistedStorage{}
+       }
+       defer f.Close()
+
+       var storageContent PersistedStorage
+       err = json.NewDecoder(f).Decode(&storageContent)
+       if err != nil {
+               fmt.Println(err)
+       }
+
+       return storageContent
+}
diff --git a/persistence/types.go b/persistence/types.go
new file mode 100644 (file)
index 0000000..0e130eb
--- /dev/null
@@ -0,0 +1,11 @@
+package persistence
+
+type PersistedStorage struct {
+       Windows Windows
+}
+
+type Windows []ChatWindow
+
+type ChatWindow struct {
+       width, height int
+}
index 5fbf979d912ed8d18695242068b8132905d631fb..90f0293721459f1ad7f9ddadb0da9eb28ea0e5c8 100644 (file)
@@ -2,7 +2,6 @@ package status
 
 import (
        "fyne.io/fyne/v2"
-       "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/data/binding"
        "fyne.io/fyne/v2/widget"
        "go.openfl.eu/chat-scroller/chat"
@@ -25,7 +24,7 @@ var (
 )
 
 func spawnChatWindow(app fyne.App) {
-       window := chat.NewChat(app)
+       window := chat.NewChat(app, model.Application.AddWindow())
        window.Show()
 }
 
@@ -38,7 +37,7 @@ func ableSpawnButton() {
        }
 }
 
-func CreateSetupWindow(app fyne.App) {
+func CreateSetupWindow(app fyne.App) fyne.CanvasObject {
 
        // init but after app is created
        createSpinner()
@@ -50,15 +49,6 @@ func CreateSetupWindow(app fyne.App) {
                spawnButton.Disable()
        }
 
-       statusWindow := app.NewWindow("Fl_GUI's chat scroller")
-       statusWindow.SetMaster()
-
-       statusWindow.SetContent(container.NewVBox(
-               //spinner,
-               twitchconn.Content,
-               spawnButton,
-       ))
-       statusWindow.Show()
-
        state.AddListener(binding.NewDataListener(ableSpawnButton))
+       return twitchconn.Content
 }
index b9ccc90149816c6c3e89b9c5ec69b57e258673bf..50a1ddec67c822e438daadd959abe3377f5ad398 100644 (file)
@@ -48,6 +48,5 @@ func CreateContent() {
                tokenLabel,
        )
 
-       //token.AddListener(binding.NewDataListener(showContent))
        tokenState.AddListener(binding.NewDataListener(updateLabel))
 }
diff --git a/todo b/todo
index 4bdbb4daf8dc058a7e36ccdfa7a7c7a8bba546e4..ca0bbac325f61bd0458e0fe0a418cc07239d139b 100644 (file)
--- a/todo
+++ b/todo
@@ -1,6 +1,4 @@
 --TODO
 
-better emotes showing
-chat settings
 persistence
 log levels?
diff --git a/token/authenticate.go b/token/authenticate.go
new file mode 100644 (file)
index 0000000..a44114f
--- /dev/null
@@ -0,0 +1,48 @@
+package token
+
+import (
+       "fmt"
+       "net/url"
+       "sync"
+
+       "fyne.io/fyne/v2"
+       "go.openfl.eu/chat-scroller/model"
+       "go.openfl.eu/twitch-auth/implicit"
+)
+
+var authMutex sync.Mutex
+
+func Authenticate(a *fyne.App) {
+       authMutex.Lock()
+       defer authMutex.Unlock()
+       state, _ := model.Application.Twitch.TokenState.Get()
+       if state == model.Valid {
+               return
+       }
+
+       authUrl, tokenChannel := implicit.AuthenticateWithHandler(&implicit.Config{
+               "9mcopb33ssgli53u6cor8ou2pvyb0g",
+               ":4567",
+               []string{"user:read:chat", "user:write:chat", "chat:read"},
+               false,
+       }, &OAuthSuccessHandler{})
+
+       app := fyne.CurrentApp()
+       u, err := url.Parse(authUrl)
+       if err != nil {
+               panic(err)
+       }
+       app.OpenURL(u)
+
+       go func() {
+               for token := range tokenChannel {
+                       if token.Err == nil {
+                               model.Application.Twitch.Token.Set(token.AccessCode)
+                       } else {
+                               model.Application.Twitch.TokenState.Set(model.Invalid)
+                               err := token.Err
+                               fmt.Println(err)
+                       }
+               }
+       }()
+}
diff --git a/token/authhandler.go b/token/authhandler.go
new file mode 100644 (file)
index 0000000..ac2c981
--- /dev/null
@@ -0,0 +1,16 @@
+package token
+
+import (
+       _ "embed"
+       "net/http"
+)
+
+//go:embed page.html
+var oathSuccessPage []byte
+
+type OAuthSuccessHandler struct {
+}
+
+func (*OAuthSuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       w.Write(oathSuccessPage)
+}
diff --git a/token/page.html b/token/page.html
new file mode 100644 (file)
index 0000000..f6a1c09
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+p {
+  font-family: sans-serif;
+}
+</style>
+</head>
+
+<body>
+  <p>
+  Successfully authenticated with twitch. You can close this page now.
+  </p>
+  <p>
+  Or you can show your love through Ko-fi:
+  <script type='text/javascript' src='https://storage.ko-fi.com/cdn/widget/Widget_2.js'></script><script type='text/javascript'>kofiwidget2.init('Support me on Ko-fi', '#69c2a7', 'N4N2XG5FJ');kofiwidget2.draw();</script>
+  </p>
+</body>
+</html>
diff --git a/windowconfig/windowconfig.go b/windowconfig/windowconfig.go
new file mode 100644 (file)
index 0000000..b0bae48
--- /dev/null
@@ -0,0 +1,30 @@
+package windowconfig
+
+import (
+       "log"
+
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/container"
+       "go.openfl.eu/chat-scroller/model"
+)
+
+type windowConfig struct {
+       *container.DocTabs
+}
+
+func CreateWindowConfig(app fyne.App) fyne.CanvasObject {
+       createTab := func() *container.TabItem {
+               window := model.Application.AddWindow()
+               windowItem, err := newWindowConfigItem(app, window)
+               if err != nil {
+                       log.Printf("Could not create new window setup: %s\n", err)
+                       return nil
+               }
+               return windowItem.TabItem
+       }
+       w := windowConfig{container.NewDocTabs(createTab())}
+       w.CreateTab = createTab
+
+       // todo intersect OnClosed
+       return w
+}
diff --git a/windowconfig/windowconfigitem.go b/windowconfig/windowconfigitem.go
new file mode 100644 (file)
index 0000000..bf56133
--- /dev/null
@@ -0,0 +1,59 @@
+package windowconfig
+
+import (
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/container"
+       "fyne.io/fyne/v2/widget"
+       "go.openfl.eu/chat-scroller/chat"
+       "go.openfl.eu/chat-scroller/fynepatch"
+       "go.openfl.eu/chat-scroller/model"
+)
+
+type WindowConfigItem struct {
+       *container.TabItem
+       Window *model.Window
+}
+
+func newWindowConfigItem(app fyne.App, window *model.Window) (WindowConfigItem, error) {
+       content := fyne.NewContainerWithLayout(
+               fynepatch.NewKeyValueLayout(),
+               widget.NewLabel("window title"),
+               windowTitleEntry(window),
+
+               widget.NewLabel("channel"),
+               channelEntry(window),
+       )
+
+       name, err := window.Name.Get()
+
+       return WindowConfigItem{
+               container.NewTabItem(name,
+                       container.NewVBox(
+                               content,
+                               widget.NewButton("open", func() {
+                                       w := chat.NewChat(app, window)
+                                       w.Show()
+                               }),
+                       ),
+               ),
+               window,
+       }, err
+}
+
+func windowTitleEntry(window *model.Window) *widget.Entry {
+       entry := widget.NewEntry()
+       entry.Text = "chat"
+       entry.OnChanged = func(l string) {
+               window.Name.Set(l)
+       }
+       return entry
+}
+
+func channelEntry(window *model.Window) *widget.Entry {
+       entry := widget.NewEntry()
+       entry.PlaceHolder = "Leave blank for own channel. TODO implement"
+       entry.OnSubmitted = func(l string) {
+               window.Channel.Set(l)
+       }
+       return entry
+}