diff --git a/README.md b/README.md index 4f3d17c8..a5cdf46a 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,28 @@ h := widget.NewHexWidget() h.Set(0xf) ``` -## Validation +## Data Binding + +Community contributed data sources for binding. + +`import fyne.io/x/fyne/data/binding` + +### WebString + +A `WebSocketString` binding creates a `String` data binding to the specified web socket URL. +Each time a message is read the value will be converted to a `string` and set on the binding. +It is also `Closable` so you should be sure to call `Close()` once you are completed using it. + +```go +s, err := binding.NewWebSocketString("wss://demo.piesocket.com/v3/channel_1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm¬ify_self") +l := widget.NewLabelWithData(s) +``` + +The code above uses a test web sockets server from "PieSocket", you can run the code above +and go to [their test page](https://www.piesocket.com/websocket-tester) to send messages. +The widget will automatically update to the latest data sent through the socket. + +## Data Validation Community contributed validators. diff --git a/data/binding/webstring.go b/data/binding/webstring.go new file mode 100644 index 00000000..8c08a67e --- /dev/null +++ b/data/binding/webstring.go @@ -0,0 +1,61 @@ +// Package binding provides extended sources of data binding. +package binding + +import ( + "io" + "log" + "net/http" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/data/binding" + + "github.com/gorilla/websocket" +) + +// StringCloser is an extension of the String interface that allows resources to be freed +// using the standard `Close()` method. +type StringCloser interface { + binding.String + io.Closer +} + +type webSocketString struct { + binding.String + conn *websocket.Conn +} + +// NewWebSocketString returns a `String` binding to a web socket server specified as `url`. +// The resulting string will be set to the content of the latest message sent through the socket. +// You should also call `Close()` on the binding once you are done to free the connection. +func NewWebSocketString(url string) (StringCloser, error) { + s := binding.NewString() + conn, _, err := websocket.DefaultDialer.Dial(url, http.Header{}) + if err != nil { + return nil, err + } + + go func() { + for { + _, p, err := conn.ReadMessage() + if err != nil { + log.Println(err) + return + } + + err = s.Set(string(p)) + if err != nil { + fyne.LogError("Failed to set string from web socket", err) + } + } + }() + ret := &webSocketString{String: s, conn: conn} + return ret, nil +} + +func (s *webSocketString) Close() error { + if s.conn == nil { + return nil + } + + return s.conn.Close() +} diff --git a/go.mod b/go.mod index d5e2eae8..6e7aaa6c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( fyne.io/fyne/v2 v2.1.0 + github.com/gorilla/websocket v1.4.2 github.com/stretchr/testify v1.7.0 github.com/wagslane/go-password-validator v0.3.0 ) diff --git a/go.sum b/go.sum index 4fe8b3ac..83584dc4 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= +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/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=