Skip to content

Add initial implementation for system navigator pop event #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ func NewApplication(opt ...Option) *Application {
config: defaultApplicationConfig,
}

// The platformPlugin and textinputPlugin are currently hardcoded as we have
// a hard link with GLFW. The plugins must be singleton and are accessed
// directly from the flutter package to wire up with glfw. If there's going
// to be a renderer interface, it's init would replace this configuration.
// The platformPlugin, textinputPlugin, etc. are currently hardcoded as we
// have a hard link with GLFW. The plugins must be singleton and are
// accessed directly from the flutter package to wire up with glfw. If
// there's going to be a renderer interface, it's init would replace this
// configuration.
opt = append(opt, AddPlugin(defaultNavigationPlugin))
opt = append(opt, AddPlugin(defaultPlatformPlugin))
opt = append(opt, AddPlugin(defaultTextinputPlugin))
opt = append(opt, AddPlugin(defaultLifecyclePlugin))

// apply all configs
for _, o := range opt {
Expand Down Expand Up @@ -180,6 +182,8 @@ func (a *Application) Run() error {
a.window.SetKeyCallback(defaultTextinputPlugin.glfwKeyCallback)
a.window.SetCharCallback(defaultTextinputPlugin.glfwCharCallback)

a.window.SetIconifyCallback(defaultLifecyclePlugin.glfwIconifyCallback)

a.window.SetCursorEnterCallback(m.glfwCursorEnterCallback)
a.window.SetCursorPosCallback(m.glfwCursorPosCallback)
a.window.SetMouseButtonCallback(m.glfwMouseButtonCallback)
Expand Down
40 changes: 40 additions & 0 deletions lifecycle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package flutter

import (
"fmt"

"github.com/go-flutter-desktop/go-flutter/plugin"
"github.com/go-gl/glfw/v3.2/glfw"
)

const lifecycleChannelName = "flutter/lifecycle"

// lifecyclePlugin implements flutter.Plugin and handles method calls to the
// flutter/lifecycle channel.
type lifecyclePlugin struct {
channel *plugin.BasicMessageChannel
}

// all hardcoded because theres not pluggable renderer system.
var defaultLifecyclePlugin = &lifecyclePlugin{}

var _ Plugin = &lifecyclePlugin{} // compile-time type check

func (p *lifecyclePlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
p.channel = plugin.NewBasicMessageChannel(messenger, lifecycleChannelName, plugin.StringCodec{})
return nil
}

func (p *lifecyclePlugin) glfwIconifyCallback(w *glfw.Window, iconified bool) {
var state string
switch iconified {
case true:
state = "AppLifecycleState.paused"
case false:
state = "AppLifecycleState.resumed"
}
_, err := p.channel.Send(state)
if err != nil {
fmt.Printf("go-flutter: Failed to send lifecycle event %s: %v\n", state, err)
}
}
2 changes: 1 addition & 1 deletion navigation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
const navigationChannelName = "flutter/navigation"

// navigationPlugin implements flutter.Plugin and handles method calls to the
// flutter/platform channel.
// flutter/navigation channel.
type navigationPlugin struct {
channel *plugin.MethodChannel
}
Expand Down
7 changes: 6 additions & 1 deletion platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import (
// platformPlugin implements flutter.Plugin and handles method calls to the
// flutter/platform channel.
type platformPlugin struct {
popBehavior PopBehaviorKind

messenger plugin.BinaryMessenger
glfwTasker *tasker.Tasker
window *glfw.Window
channel *plugin.MethodChannel
}

// hardcoded because there is no swappable renderer interface.
var defaultPlatformPlugin = &platformPlugin{}
var defaultPlatformPlugin = &platformPlugin{
popBehavior: PopBehaviorNone,
}

var _ Plugin = &platformPlugin{} // compile-time type check
var _ PluginGLFW = &platformPlugin{} // compile-time type check
Expand All @@ -37,6 +41,7 @@ func (p *platformPlugin) InitPluginGLFW(window *glfw.Window) (err error) {
p.channel.HandleFunc("Clipboard.setData", p.handleClipboardSetData)
p.channel.HandleFunc("Clipboard.getData", p.handleClipboardGetData)
p.channel.HandleFunc("SystemChrome.setApplicationSwitcherDescription", p.handleWindowSetTitle)
p.channel.HandleFunc("SystemNavigator.pop", p.handleSystemNavigatorPop)

return nil
}
Expand Down
9 changes: 6 additions & 3 deletions plugin/method-channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ func (m *MethodChannel) InvokeMethod(name string, arguments interface{}) (result
if err != nil {
return nil, errors.Wrap(err, "failed to send methodcall")
}
// TODO(GeertJohan): InvokeMethod may not return any JSON. In Java this is
// handled by not having a callback handler, which means no reponse is
// expected and reponse is never unmarshalled. We should perhaps define
// InvokeMethod(..) and InovkeMethodNoResponse(..) to avoid errors when no
// response is given.
// https://github.com/go-flutter-desktop/go-flutter/issues/141
result, err = m.methodCodec.DecodeEnvelope(encodedReply)
if flutterError, ok := err.(*FlutterError); ok {
return nil, flutterError
}
if err != nil {
return nil, err
}
Expand Down
67 changes: 67 additions & 0 deletions pop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package flutter

import (
"fmt"

"github.com/pkg/errors"
)

// PopBehaviorKind defines how an application should handle the navigation pop
// event from the flutter side.
type PopBehaviorKind int

const (
// PopBehaviorNone means the system navigation pop event is ignored.
PopBehaviorNone PopBehaviorKind = iota
// PopBehaviorHide hides the application window on a system navigation pop
// event.
PopBehaviorHide
// PopBehaviorIconify minimizes/iconifies the application window on a system
// navigation pop event.
PopBehaviorIconify
// PopBehaviorClose closes the application on a system navigation pop event.
PopBehaviorClose
)

// PopBehavior sets the PopBehavior on the application
func PopBehavior(popBehavior PopBehaviorKind) Option {
return func(c *config) {
// TODO: this is a workarround because there is no renderer interface
// yet. We rely on a platform plugin singleton to handle events from the
// flutter side. Should go via Application and renderer abstraction
// layer.
//
// Downside of this workarround is that it will configure the pop
// behavior for all Application's within the same Go process.
defaultPlatformPlugin.popBehavior = popBehavior
}
}

func (p *platformPlugin) handleSystemNavigatorPop(arguments interface{}) (reply interface{}, err error) {
switch p.popBehavior {
case PopBehaviorNone:
return nil, nil
case PopBehaviorHide:
p.glfwTasker.Do(func() {
p.window.Hide()
})
return nil, nil
case PopBehaviorIconify:
var err error
p.glfwTasker.Do(func() {
err = p.window.Iconify()
})
if err != nil {
fmt.Printf("go-flutter: error on iconifying window: %v\n", err)
return nil, errors.Wrap(err, "failed to iconify window")
}
return nil, nil
case PopBehaviorClose:
p.glfwTasker.Do(func() {
p.window.SetShouldClose(true)
})
return nil, nil
default:
return nil, errors.Errorf("unknown pop behavior %T not implemented by platform handler", p.popBehavior)
}
}