mirror of
https://github.com/netbirdio/systray.git
synced 2026-05-22 17:08:41 -07:00
Merge pull request #97 from getlantern/ox/issue452
Add support for app window
This commit is contained in:
@@ -72,6 +72,35 @@ sudo apt-get install libgtk-3-dev libappindicator3-dev
|
||||
go build -ldflags -H=windowsgui
|
||||
```
|
||||
|
||||
You'll also need to include a Manifest for your windows app. Assuming your executable is named `app.exe`, create a file `app.manifest` like this:
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="App Name Here" type="win32"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
```
|
||||
|
||||
Then either compile the manifest using the [rsrc](https://github.com/akavel/rsrc) tool, like this:
|
||||
|
||||
```
|
||||
go get github.com/akavel/rsrc
|
||||
rsrc -manifest app.manifest -o rsrc.syso
|
||||
```
|
||||
|
||||
or rename the app.manifest file to app.exe.manifest and distribute it with the application instead.
|
||||
|
||||
### macOS
|
||||
|
||||
On macOS, you will need to create an application bundle to wrap the binary; simply folders with the following minimal structure and assets:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="Lantern" type="win32"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
+3
-4
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/getlantern/systray"
|
||||
"github.com/getlantern/systray/example/icon"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -18,7 +17,7 @@ func main() {
|
||||
fmt.Println("Finished onExit")
|
||||
}
|
||||
// Should be called at the very beginning of main().
|
||||
systray.Run(onReady, onExit)
|
||||
systray.RunWithAppWindow("Lantern", 1024, 768, onReady, onExit)
|
||||
}
|
||||
|
||||
func onReady() {
|
||||
@@ -42,7 +41,7 @@ func onReady() {
|
||||
mChecked := systray.AddMenuItem("Unchecked", "Check Me")
|
||||
mEnabled := systray.AddMenuItem("Enabled", "Enabled")
|
||||
systray.AddMenuItem("Ignored", "Ignored")
|
||||
mUrl := systray.AddMenuItem("Open Lantern.org", "my home")
|
||||
mUrl := systray.AddMenuItem("Open UI", "my home")
|
||||
mQuit := systray.AddMenuItem("退出", "Quit the whole app")
|
||||
|
||||
// Sets the icon of a menu item. Only available on Mac.
|
||||
@@ -67,7 +66,7 @@ func onReady() {
|
||||
mEnabled.SetTitle("Disabled")
|
||||
mEnabled.Disable()
|
||||
case <-mUrl.ClickedCh:
|
||||
open.Run("https://www.getlantern.org")
|
||||
systray.ShowAppWindow("https://www.getlantern.org")
|
||||
case <-mToggle.ClickedCh:
|
||||
if shown {
|
||||
mQuitOrig.Hide()
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
module github.com/getlantern/systray
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7
|
||||
github.com/getlantern/uuid v1.2.0
|
||||
github.com/lxn/walk v0.0.0-20190919092010-4386a6d42094
|
||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13 // indirect
|
||||
gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk=
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/uuid v1.2.0 h1:pGrGaCV7XEaG6lvjWkwf8Y92BjB/9yFmkKsNFpRQ7rc=
|
||||
github.com/getlantern/uuid v1.2.0/go.mod h1:uX10hOzZUUDR+oYNSIks+RcozOEiwTNC/K2rw9SUi1k=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/lxn/walk v0.0.0-20190919092010-4386a6d42094 h1:axV9dR1/LfxQTFTpzjiBzQV9tFarx+Evs2ONi/ceq2U=
|
||||
github.com/lxn/walk v0.0.0-20190919092010-4386a6d42094/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 h1:RVMGIuuNgrpGB7I79f6xfhGCkpN47IaEGh8VTM0p7Xc=
|
||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e h1:VAzdS5Nw68fbf5RZ8RDVlUvPXNU6Z3jtPCK/qvm4FoQ=
|
||||
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13 h1:/zi0zzlPHWXYXrO1LjNRByFu8sdGgCkj2JLDdBIB84k=
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
|
||||
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
|
||||
+7
-3
@@ -50,10 +50,14 @@ var (
|
||||
)
|
||||
|
||||
// Run initializes GUI and starts the event loop, then invokes the onReady
|
||||
// callback.
|
||||
// It blocks until systray.Quit() is called.
|
||||
// callback. It blocks until systray.Quit() is called.
|
||||
// Should be called at the very beginning of main() to lock at main thread.
|
||||
func Run(onReady func(), onExit func()) {
|
||||
RunWithAppWindow("", 0, 0, onReady, onExit)
|
||||
}
|
||||
|
||||
// RunWithAppWindow is like Run but also enables an application window with the given title.
|
||||
func RunWithAppWindow(title string, width int, height int, onReady func(), onExit func()) {
|
||||
runtime.LockOSThread()
|
||||
atomic.StoreInt64(&hasStarted, 1)
|
||||
|
||||
@@ -78,7 +82,7 @@ func Run(onReady func(), onExit func()) {
|
||||
}
|
||||
systrayExit = onExit
|
||||
|
||||
nativeLoop()
|
||||
nativeLoop(title, width, height)
|
||||
}
|
||||
|
||||
// Quit the systray
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
extern void systray_ready();
|
||||
extern void systray_on_exit();
|
||||
extern void systray_menu_item_selected(int menu_id);
|
||||
int nativeLoop(void);
|
||||
int nativeLoop(char* title, int width, int height);
|
||||
|
||||
void setIcon(const char* iconBytes, int length);
|
||||
void setMenuItemIcon(const char* iconBytes, int length, int menuId);
|
||||
void setTitle(char* title);
|
||||
void setTooltip(char* tooltip);
|
||||
void configureAppWindow(char* title, int width, int height);
|
||||
void showAppWindow(char* url);
|
||||
void add_or_update_menu_item(int menuId, char* title, char* tooltip, short disabled, short checked);
|
||||
void add_separator(int menuId);
|
||||
void hide_menu_item(int menuId);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#include "systray.h"
|
||||
|
||||
NSWindowController *windowController = nil;
|
||||
WKWebView *webView = nil;
|
||||
|
||||
void configureAppWindow(char* title, int width, int height)
|
||||
{
|
||||
if (windowController != nil) {
|
||||
// already configured, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
NSRect frame = NSMakeRect(0, 0, width, height);
|
||||
int mask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable;
|
||||
NSWindow *window =
|
||||
[[NSWindow alloc] initWithContentRect:frame
|
||||
styleMask:mask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[window setTitle:[[NSString alloc] initWithUTF8String:title]];
|
||||
[window makeKeyAndOrderFront:nil];
|
||||
[window center];
|
||||
|
||||
NSView *contentView = [window contentView];
|
||||
webView = [[WKWebView alloc] initWithFrame:[contentView bounds]];
|
||||
[webView setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[contentView addSubview:webView];
|
||||
[contentView addConstraint:
|
||||
[NSLayoutConstraint constraintWithItem:webView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:contentView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
[contentView addConstraint:
|
||||
[NSLayoutConstraint constraintWithItem:webView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:contentView
|
||||
attribute:NSLayoutAttributeHeight
|
||||
multiplier:1
|
||||
constant:0]];
|
||||
|
||||
// Window controller:
|
||||
windowController = [[NSWindowController alloc] initWithWindow:window];
|
||||
free(title);
|
||||
}
|
||||
|
||||
void doShowAppWindow(char* url)
|
||||
{
|
||||
if (windowController == nil) {
|
||||
// no app window to open
|
||||
return;
|
||||
}
|
||||
id nsURL = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]];
|
||||
id req = [[NSURLRequest alloc] initWithURL: nsURL
|
||||
cachePolicy: NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval: 5];
|
||||
[webView loadRequest:req];
|
||||
[windowController.window orderFrontRegardless];
|
||||
free(url);
|
||||
}
|
||||
|
||||
void showAppWindow(char* url)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
doShowAppWindow(url);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit2/webkit2.h>
|
||||
|
||||
static GtkWindow *web_window = NULL;
|
||||
static WebKitWebView *web_view = NULL;
|
||||
|
||||
static gint x, y;
|
||||
static bool needsMove = false;
|
||||
|
||||
gboolean on_window_deleted(GtkWidget *window, GdkEvent *event, gpointer data)
|
||||
{
|
||||
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
|
||||
needsMove = true;
|
||||
gtk_widget_hide(window);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void configureAppWindow(char* title, int width, int height)
|
||||
{
|
||||
// Create an 800x600 window that will contain the browser instance
|
||||
web_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
|
||||
gtk_window_set_title(web_window, title);
|
||||
gtk_window_set_default_size(web_window, width, height);
|
||||
gtk_window_set_skip_taskbar_hint (web_window, TRUE);
|
||||
g_signal_connect(G_OBJECT(web_window), "delete-event", G_CALLBACK(on_window_deleted), NULL);
|
||||
|
||||
// Create a browser instance
|
||||
web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
|
||||
|
||||
// Put the browser area into the web window
|
||||
gtk_container_add(GTK_CONTAINER(web_window), GTK_WIDGET(web_view));
|
||||
|
||||
// Make sure that when the browser area becomes visible, it will get mouse
|
||||
// and keyboard events
|
||||
gtk_widget_grab_focus(GTK_WIDGET(web_view));
|
||||
free(title);
|
||||
}
|
||||
|
||||
gboolean do_show_app_window(gpointer data)
|
||||
{
|
||||
gtk_widget_show_all(GTK_WIDGET(web_window));
|
||||
if (needsMove) {
|
||||
gtk_window_move(web_window, x, y);
|
||||
needsMove = false;
|
||||
}
|
||||
gtk_window_present(web_window);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void showAppWindow(char* url)
|
||||
{
|
||||
// Load a web page into the browser instance
|
||||
webkit_web_view_load_uri(web_view, url);
|
||||
|
||||
g_idle_add(do_show_app_window, NULL);
|
||||
free(url);
|
||||
}
|
||||
+11
-1
@@ -70,6 +70,11 @@
|
||||
systray_ready();
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
systray_on_exit();
|
||||
@@ -182,9 +187,14 @@
|
||||
|
||||
@end
|
||||
|
||||
int nativeLoop(void) {
|
||||
void configureAppWindow(char* title, int width, int height);
|
||||
|
||||
int nativeLoop(char* title, int width, int height) {
|
||||
AppDelegate *delegate = [[AppDelegate alloc] init];
|
||||
[[NSApplication sharedApplication] setDelegate:delegate];
|
||||
if (strcmp(title, "") != 0) {
|
||||
configureAppWindow(title, width, height);
|
||||
}
|
||||
[NSApp run];
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+6
-1
@@ -23,7 +23,9 @@ typedef struct {
|
||||
short checked;
|
||||
} MenuItemInfo;
|
||||
|
||||
int nativeLoop(void) {
|
||||
void prepareBrowser();
|
||||
|
||||
int nativeLoop(char* title, int width, int height) {
|
||||
gtk_init(0, NULL);
|
||||
global_app_indicator = app_indicator_new("systray", "",
|
||||
APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
|
||||
@@ -31,6 +33,9 @@ int nativeLoop(void) {
|
||||
global_tray_menu = gtk_menu_new();
|
||||
app_indicator_set_menu(global_app_indicator, GTK_MENU(global_tray_menu));
|
||||
systray_ready();
|
||||
if (strcmp(title, "") != 0) {
|
||||
configureAppWindow(title, width, height);
|
||||
}
|
||||
gtk_main();
|
||||
systray_on_exit();
|
||||
return 0;
|
||||
|
||||
+11
-4
@@ -3,9 +3,10 @@
|
||||
package systray
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 appindicator3-0.1
|
||||
#cgo linux CFLAGS: -DWEBVIEW_GTK=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 appindicator3-0.1
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
|
||||
#cgo darwin LDFLAGS: -framework Cocoa
|
||||
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
||||
|
||||
#include "systray.h"
|
||||
*/
|
||||
@@ -15,14 +16,20 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func nativeLoop() {
|
||||
C.nativeLoop()
|
||||
func nativeLoop(title string, width int, height int) {
|
||||
C.nativeLoop(C.CString(title), C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func quit() {
|
||||
C.quit()
|
||||
}
|
||||
|
||||
// ShowAppWindow shows the given URL in the application window. Only works if
|
||||
// configureAppWindow has been called first.
|
||||
func ShowAppWindow(url string) {
|
||||
C.showAppWindow(C.CString(url))
|
||||
}
|
||||
|
||||
// SetIcon sets the systray icon.
|
||||
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
|
||||
// for other platforms.
|
||||
|
||||
+120
-661
File diff suppressed because it is too large
Load Diff
@@ -1,132 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const iconFilePath = "example/icon/iconwin.ico"
|
||||
|
||||
func TestBaseWindowsTray(t *testing.T) {
|
||||
systrayReady = func() {}
|
||||
systrayExit = func() {}
|
||||
|
||||
runtime.LockOSThread()
|
||||
|
||||
if err := wt.initInstance(); err != nil {
|
||||
t.Fatalf("initInstance failed: %s", err)
|
||||
}
|
||||
|
||||
if err := wt.createMenu(); err != nil {
|
||||
t.Fatalf("createMenu failed: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
pDestroyWindow.Call(uintptr(wt.window))
|
||||
wt.wcex.unregister()
|
||||
}()
|
||||
|
||||
if err := wt.setIcon(iconFilePath); err != nil {
|
||||
t.Errorf("SetIcon failed: %s", err)
|
||||
}
|
||||
|
||||
if err := wt.setTooltip("Cyrillic tooltip тест:)"); err != nil {
|
||||
t.Errorf("SetIcon failed: %s", err)
|
||||
}
|
||||
|
||||
var id int32 = 0
|
||||
err := wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple enabled", false, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple disabled", true, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addSeparatorMenuItem(atomic.AddInt32(&id, 1))
|
||||
if err != nil {
|
||||
t.Errorf("addSeparatorMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple checked enabled", false, true)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple checked disabled", true, true)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
err = wt.hideMenuItem(1)
|
||||
if err != nil {
|
||||
t.Errorf("hideMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
err = wt.hideMenuItem(100)
|
||||
if err == nil {
|
||||
t.Error("hideMenuItem failed: must return error on invalid item id")
|
||||
}
|
||||
|
||||
err = wt.addOrUpdateMenuItem(2, "Simple disabled update", true, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
time.AfterFunc(1*time.Second, quit)
|
||||
|
||||
m := struct {
|
||||
WindowHandle windows.Handle
|
||||
Message uint32
|
||||
Wparam uintptr
|
||||
Lparam uintptr
|
||||
Time uint32
|
||||
Pt point
|
||||
}{}
|
||||
for {
|
||||
ret, _, err := pGetMessage.Call(uintptr(unsafe.Pointer(&m)), 0, 0, 0)
|
||||
res := int32(ret)
|
||||
if res == -1 {
|
||||
t.Errorf("win32 GetMessage failed: %v", err)
|
||||
return
|
||||
} else if res == 0 {
|
||||
break
|
||||
}
|
||||
pTranslateMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
pDispatchMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowsRun(t *testing.T) {
|
||||
onReady := func() {
|
||||
b, err := ioutil.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't load icon file: %v", err)
|
||||
}
|
||||
SetIcon(b)
|
||||
SetTitle("Test title с кириллицей")
|
||||
|
||||
bSomeBtn := AddMenuItem("Йа кнопко", "")
|
||||
bSomeBtn.Check()
|
||||
AddSeparator()
|
||||
bQuit := AddMenuItem("Quit", "Quit the whole app")
|
||||
go func() {
|
||||
<-bQuit.ClickedCh
|
||||
t.Log("Quit reqested")
|
||||
Quit()
|
||||
}()
|
||||
time.AfterFunc(1*time.Second, Quit)
|
||||
}
|
||||
|
||||
onExit := func() {
|
||||
t.Log("Exit success")
|
||||
}
|
||||
|
||||
Run(onReady, onExit)
|
||||
}
|
||||
Reference in New Issue
Block a user