Skip to content

Commit f6bb28c

Browse files
committed
Add initial implementation for system navigator pop event
1 parent a633350 commit f6bb28c

File tree

6 files changed

+128
-9
lines changed

6 files changed

+128
-9
lines changed

application.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ func NewApplication(opt ...Option) *Application {
3636
config: defaultApplicationConfig,
3737
}
3838

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

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

185+
a.window.SetIconifyCallback(defaultLifecyclePlugin.glfwIconifyCallback)
186+
183187
a.window.SetCursorEnterCallback(m.glfwCursorEnterCallback)
184188
a.window.SetCursorPosCallback(m.glfwCursorPosCallback)
185189
a.window.SetMouseButtonCallback(m.glfwMouseButtonCallback)

lifecycle.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package flutter
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/go-flutter-desktop/go-flutter/plugin"
7+
"github.com/go-gl/glfw/v3.2/glfw"
8+
)
9+
10+
const lifecycleChannelName = "flutter/lifecycle"
11+
12+
// lifecyclePlugin implements flutter.Plugin and handles method calls to the
13+
// flutter/lifecycle channel.
14+
type lifecyclePlugin struct {
15+
channel *plugin.BasicMessageChannel
16+
}
17+
18+
// all hardcoded because theres not pluggable renderer system.
19+
var defaultLifecyclePlugin = &lifecyclePlugin{}
20+
21+
var _ Plugin = &lifecyclePlugin{} // compile-time type check
22+
23+
func (p *lifecyclePlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
24+
p.channel = plugin.NewBasicMessageChannel(messenger, lifecycleChannelName, plugin.StringCodec{})
25+
return nil
26+
}
27+
28+
func (p *lifecyclePlugin) glfwIconifyCallback(w *glfw.Window, iconified bool) {
29+
var state string
30+
switch iconified {
31+
case true:
32+
state = "AppLifecycleState.paused"
33+
case false:
34+
state = "AppLifecycleState.resumed"
35+
}
36+
_, err := p.channel.Send(state)
37+
if err != nil {
38+
fmt.Printf("go-flutter: Failed to send lifecycle event %s: %v\n", state, err)
39+
}
40+
}

navigation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
const navigationChannelName = "flutter/navigation"
88

99
// navigationPlugin implements flutter.Plugin and handles method calls to the
10-
// flutter/platform channel.
10+
// flutter/navigation channel.
1111
type navigationPlugin struct {
1212
channel *plugin.MethodChannel
1313
}

platform.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ import (
1313
// platformPlugin implements flutter.Plugin and handles method calls to the
1414
// flutter/platform channel.
1515
type platformPlugin struct {
16+
popBehavior PopBehaviorKind
17+
1618
messenger plugin.BinaryMessenger
1719
glfwTasker *tasker.Tasker
1820
window *glfw.Window
1921
channel *plugin.MethodChannel
2022
}
2123

2224
// hardcoded because there is no swappable renderer interface.
23-
var defaultPlatformPlugin = &platformPlugin{}
25+
var defaultPlatformPlugin = &platformPlugin{
26+
popBehavior: PopBehaviorNone,
27+
}
2428

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

4146
return nil
4247
}

plugin/method-channel.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ func (m *MethodChannel) InvokeMethod(name string, arguments interface{}) (result
5555
if err != nil {
5656
return nil, errors.Wrap(err, "failed to send methodcall")
5757
}
58+
// TODO(GeertJohan): InvokeMethod may not return any JSON. In Java this is
59+
// handled by not having a callback handler, which means no reponse is
60+
// expected and reponse is never unmarshalled. We should perhaps define
61+
// InvokeMethod(..) and InovkeMethodNoResponse(..) to avoid errors when no
62+
// response is given.
63+
// https://github.com/go-flutter-desktop/go-flutter/issues/141
5864
result, err = m.methodCodec.DecodeEnvelope(encodedReply)
59-
if flutterError, ok := err.(*FlutterError); ok {
60-
return nil, flutterError
61-
}
6265
if err != nil {
6366
return nil, err
6467
}

pop.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package flutter
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/pkg/errors"
7+
)
8+
9+
// PopBehaviorKind defines how an application should handle the navigation pop
10+
// event from the flutter side.
11+
type PopBehaviorKind int
12+
13+
const (
14+
// PopBehaviorNone means the system navigation pop event is ignored.
15+
PopBehaviorNone PopBehaviorKind = iota
16+
// PopBehaviorHide hides the application window on a system navigation pop
17+
// event.
18+
PopBehaviorHide
19+
// PopBehaviorIconify iconifies the application window on a system
20+
// navigation pop event.
21+
PopBehaviorIconify
22+
// PopBehaviorClose closes the application on a system navigation pop event.
23+
PopBehaviorClose
24+
)
25+
26+
// PopBehavior sets the PopBehavior on the application
27+
func PopBehavior(popBehavior PopBehaviorKind) Option {
28+
return func(c *config) {
29+
// TODO: this is a workarround because there is no renderer interface
30+
// yet. We rely on a platform plugin singleton to handle events from the
31+
// flutter side. Should go via Application and renderer abstraction
32+
// layer.
33+
//
34+
// Downside of this workarround is that it will configure the pop
35+
// behavior for all Application's within the same Go process.
36+
defaultPlatformPlugin.popBehavior = popBehavior
37+
}
38+
}
39+
40+
func (p *platformPlugin) handleSystemNavigatorPop(arguments interface{}) (reply interface{}, err error) {
41+
switch p.popBehavior {
42+
case PopBehaviorNone:
43+
return nil, nil
44+
case PopBehaviorHide:
45+
p.glfwTasker.Do(func() {
46+
p.window.Hide()
47+
})
48+
return nil, nil
49+
case PopBehaviorIconify:
50+
var err error
51+
p.glfwTasker.Do(func() {
52+
err = p.window.Iconify()
53+
})
54+
if err != nil {
55+
fmt.Printf("go-flutter: error on iconifying window: %v\n", err)
56+
return nil, errors.Wrap(err, "failed to iconify window")
57+
}
58+
return nil, nil
59+
case PopBehaviorClose:
60+
p.glfwTasker.Do(func() {
61+
p.window.SetShouldClose(true)
62+
})
63+
return nil, nil
64+
default:
65+
return nil, errors.Errorf("unknown pop behavior %T not implemented by platform handler", p.popBehavior)
66+
}
67+
}

0 commit comments

Comments
 (0)