5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 06:51:26 +08:00
wails/v2/internal/frontend/desktop/linux/window.go
skamensky 04d35410de
Expose screen dimensions (#1519)
* get dimensions working for linux

* Cleaning up some GTK code

I was getting the following errors due to some bad casts.

Gdk-CRITICAL **: 18:58:51.943: gdk_monitor_get_geometry: assertion 'GDK_IS_MONITOR (monitor)' failed
Gdk-CRITICAL **: 18:58:51.943: gdk_display_get_monitor_at_window: assertion 'GDK_IS_DISPLAY (display)' failed

This commit fixes these errors

* Adding Screen namespace along with linux implementation

* moving ScreenGetAll into a more appropriate place

* Fixing typescript definition mistake, documentation, ordering of functions, and formatting

* add ScreenGetAll to more templates

* moving screen into its own javascript file

* fixing bug where screen objects are not returned from the runtime function

* rebuilding frontend wrapper package

* adding windows implementation of ScreenGetAll

* adding screen get all implementation for darwin

* reverting a change that is unrelated to the work on expose-dimensions

* removing duplicate comparison

* changing GetNthScreen in screen API on macos

To use frame instead of visibleframe to keep into account the space the the dock takes up
We want to include that space in the calculation in order to keep the sizes of screens consistent across platforms

* Correcting screen jsdoc

It used to say it returned a single screen object. Now it says that it returns an array of screen objects

* Fixing typo in function name

* reverting pointless spacing change

* reverting pointless spacing change

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: shmuel.kamensky <shmuel.kamensky@shutterfly.com>
2022-07-16 12:33:37 +10:00

941 lines
25 KiB
Go

//go:build linux
// +build linux
package linux
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
#include <JavaScriptCore/JavaScript.h>
#include "gtk/gtk.h"
#include "webkit2/webkit2.h"
#include <stdio.h>
#include <limits.h>
#include "stdint.h"
void ExecuteOnMainThread(void* f, gpointer jscallback) {
g_idle_add((GSourceFunc)f, (gpointer)jscallback);
}
static GtkWidget* GTKWIDGET(void *pointer) {
return GTK_WIDGET(pointer);
}
static GtkWindow* GTKWINDOW(void *pointer) {
return GTK_WINDOW(pointer);
}
static GtkContainer* GTKCONTAINER(void *pointer) {
return GTK_CONTAINER(pointer);
}
static GtkBox* GTKBOX(void *pointer) {
return GTK_BOX(pointer);
}
static void SetMinMaxSize(GtkWindow* window, int min_width, int min_height, int max_width, int max_height) {
GdkGeometry size;
size.min_width = size.min_height = size.max_width = size.max_height = 0;
int flags = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE;
size.max_height = (max_height == 0 ? INT_MAX : max_height);
size.max_width = (max_width == 0 ? INT_MAX : max_width);
size.min_height = min_height;
size.min_width = min_width;
gtk_window_set_geometry_hints(window, NULL, &size, flags);
}
GdkMonitor* getCurrentMonitor(GtkWindow *window) {
// Get the monitor that the window is currently on
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
GdkMonitor *monitor = gdk_display_get_monitor_at_window(display, gdk_window);
return GDK_MONITOR(monitor);
}
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
GdkMonitor *monitor = getCurrentMonitor(window);
// Get the geometry of the monitor
GdkRectangle result;
gdk_monitor_get_geometry (monitor,&result);
return result;
}
int getCurrentMonitorScaleFactor(GtkWindow *window) {
GdkMonitor *monitor = getCurrentMonitor(window);
return gdk_monitor_get_scale_factor(monitor);
}
gboolean Center(gpointer data) {
GtkWindow *window = (GtkWindow*)data;
// Get the geometry of the monitor
GdkRectangle m = getCurrentMonitorGeometry(window);
// Get the window width/height
int windowWidth, windowHeight;
gtk_window_get_size(window, &windowWidth, &windowHeight);
int newX = ((m.width - windowWidth) / 2) + m.x;
int newY = ((m.height - windowHeight) / 2) + m.y;
// Place the window at the center of the monitor
gtk_window_move(window, newX, newY);
return G_SOURCE_REMOVE;
}
int IsFullscreen(GtkWidget *widget) {
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_FULLSCREEN;
}
int IsMaximised(GtkWidget *widget) {
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_MAXIMIZED;
}
extern void processMessage(char*);
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
WebKitJavascriptResult *result,
void* data)
{
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
JSCValue *value = webkit_javascript_result_get_js_value(result);
char *message = jsc_value_to_string(value);
#else
JSGlobalContextRef context = webkit_javascript_result_get_global_context(result);
JSValueRef value = webkit_javascript_result_get_value(result);
JSStringRef js = JSValueToStringCopy(context, value, NULL);
size_t messageSize = JSStringGetMaximumUTF8CStringSize(js);
char *message = g_new(char, messageSize);
JSStringGetUTF8CString(js, message, messageSize);
JSStringRelease(js);
#endif
processMessage(message);
g_free(message);
}
static void webviewLoadChanged(WebKitWebView *web_view, WebKitLoadEvent load_event, gpointer data)
{
if (load_event == WEBKIT_LOAD_FINISHED) {
processMessage("DomReady");
}
}
ulong setupInvokeSignal(void* contentManager) {
return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL);
}
// These are the x,y & time of the last mouse down event
// It's used for window dragging
float xroot = 0.0f;
float yroot = 0.0f;
int dragTime = -1;
bool contextMenuDisabled = false;
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void* dummy)
{
if( event == NULL ) {
xroot = yroot = 0.0f;
dragTime = -1;
return FALSE;
}
if( event->button == 3 && contextMenuDisabled ) {
return TRUE;
}
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
{
xroot = event->x_root;
yroot = event->y_root;
dragTime = event->time;
}
return FALSE;
}
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, void* dummy)
{
if (event == NULL || (event->type == GDK_BUTTON_RELEASE && event->button == 1))
{
xroot = yroot = 0.0f;
dragTime = -1;
}
return FALSE;
}
void connectButtons(void* webview) {
g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-press-event", G_CALLBACK(buttonPress), NULL);
g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-release-event", G_CALLBACK(buttonRelease), NULL);
}
extern void processURLRequest(void *request);
// This is called when the close button on the window is pressed
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data)
{
processMessage("Q");
// since we handle the close in processMessage tell GTK to not invoke additional handlers - see:
// https://docs.gtk.org/gtk3/signal.Widget.delete-event.html
return TRUE;
}
GtkWidget* setupWebview(void* contentManager, GtkWindow* window, int hideWindowOnClose) {
GtkWidget* webview = webkit_web_view_new_with_user_content_manager((WebKitUserContentManager*)contentManager);
//gtk_container_add(GTK_CONTAINER(window), webview);
WebKitWebContext *context = webkit_web_context_get_default();
webkit_web_context_register_uri_scheme(context, "wails", (WebKitURISchemeRequestCallback)processURLRequest, NULL, NULL);
g_signal_connect(G_OBJECT(webview), "load-changed", G_CALLBACK(webviewLoadChanged), NULL);
if (hideWindowOnClose) {
g_signal_connect(GTK_WIDGET(window), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
} else {
g_signal_connect(GTK_WIDGET(window), "delete-event", G_CALLBACK(close_button_pressed), NULL);
}
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
webkit_settings_set_user_agent_with_application_details(settings, "wails.io", "");
return webview;
}
void devtoolsEnabled(void* webview, int enabled) {
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
gboolean genabled = enabled == 1 ? true : false;
webkit_settings_set_enable_developer_extras(settings, genabled);
}
void loadIndex(void* webview, char* url) {
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
}
typedef struct DragOptions {
void *webview;
GtkWindow* mainwindow;
} DragOptions;
static gboolean startDrag(gpointer data) {
DragOptions* options = (DragOptions*)data;
// Ignore non-toplevel widgets
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(options->webview));
if (!GTK_IS_WINDOW(window)) {
free(data);
return G_SOURCE_REMOVE;
}
gtk_window_begin_move_drag(options->mainwindow, 1, xroot, yroot, dragTime);
free(data);
return G_SOURCE_REMOVE;
}
static void StartDrag(void *webview, GtkWindow* mainwindow) {
DragOptions* data = malloc(sizeof(DragOptions));
data->webview = webview;
data->mainwindow = mainwindow;
ExecuteOnMainThread(startDrag, (gpointer)data);
}
typedef struct JSCallback {
void* webview;
char* script;
} JSCallback;
void executeJS(void *data) {
struct JSCallback *js = data;
webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL);
free(js->script);
}
void extern processMessageDialogResult(char*);
typedef struct MessageDialogOptions {
void* window;
char* title;
char* message;
int messageType;
} MessageDialogOptions;
void messageDialog(void *data) {
GtkDialogFlags flags;
GtkMessageType messageType;
MessageDialogOptions *options = (MessageDialogOptions*) data;
if( options->messageType == 0 ) {
messageType = GTK_MESSAGE_INFO;
flags = GTK_BUTTONS_OK;
} else if( options->messageType == 1 ) {
messageType = GTK_MESSAGE_ERROR;
flags = GTK_BUTTONS_OK;
} else if( options->messageType == 2 ) {
messageType = GTK_MESSAGE_QUESTION;
flags = GTK_BUTTONS_YES_NO;
} else {
messageType = GTK_MESSAGE_WARNING;
flags = GTK_BUTTONS_OK;
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(options->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
messageType,
flags,
options->message, NULL);
gtk_window_set_title(GTK_WINDOW(dialog), options->title);
GtkResponseType result = gtk_dialog_run(GTK_DIALOG(dialog));
if ( result == GTK_RESPONSE_YES ) {
processMessageDialogResult("Yes");
} else if ( result == GTK_RESPONSE_NO ) {
processMessageDialogResult("No");
} else if ( result == GTK_RESPONSE_OK ) {
processMessageDialogResult("OK");
} else if ( result == GTK_RESPONSE_CANCEL ) {
processMessageDialogResult("Cancel");
} else {
processMessageDialogResult("");
}
gtk_widget_destroy(dialog);
free(options->title);
free(options->message);
}
void extern processOpenFileResult(void*);
typedef struct OpenFileDialogOptions {
GtkWindow* window;
char* title;
char* defaultFilename;
char* defaultDirectory;
int createDirectories;
int multipleFiles;
int showHiddenFiles;
GtkFileChooserAction action;
GtkFileFilter** filters;
} OpenFileDialogOptions;
GtkFileFilter** allocFileFilterArray(size_t ln) {
return (GtkFileFilter**) malloc(ln * sizeof(GtkFileFilter*));
}
void freeFileFilterArray(GtkFileFilter** filters) {
free(filters);
}
void opendialog(void *data) {
struct OpenFileDialogOptions *options = data;
char *label = "_Open";
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
label = "_Save";
}
GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->window, options->action,
"_Cancel", GTK_RESPONSE_CANCEL,
label, GTK_RESPONSE_ACCEPT,
NULL);
GtkFileChooser *fc = GTK_FILE_CHOOSER(dlgWidget);
// filters
if (options->filters != 0) {
int index = 0;
GtkFileFilter* thisFilter;
while(options->filters[index] != NULL) {
thisFilter = options->filters[index];
gtk_file_chooser_add_filter(fc, thisFilter);
index++;
}
}
gtk_file_chooser_set_local_only(fc, FALSE);
if (options->multipleFiles == 1) {
gtk_file_chooser_set_select_multiple(fc, TRUE);
}
gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE);
if (options->createDirectories == 1) {
gtk_file_chooser_set_create_folders(fc, TRUE);
}
if (options->showHiddenFiles == 1) {
gtk_file_chooser_set_show_hidden(fc, TRUE);
}
if (options->defaultDirectory != NULL) {
gtk_file_chooser_set_current_folder (fc, options->defaultDirectory);
free(options->defaultDirectory);
}
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
if (options->defaultFilename != NULL) {
gtk_file_chooser_set_current_name(fc, options->defaultFilename);
free(options->defaultFilename);
}
}
gint response = gtk_dialog_run(GTK_DIALOG(dlgWidget));
// Max 1024 files to select
char** result = calloc(1024, sizeof(char*));
int resultIndex = 0;
if (response == GTK_RESPONSE_ACCEPT) {
GSList* filenames = gtk_file_chooser_get_filenames(fc);
GSList *iter = filenames;
while(iter) {
result[resultIndex++] = (char *)iter->data;
iter = g_slist_next(iter);
if (resultIndex == 1024) {
break;
}
}
processOpenFileResult(result);
iter = filenames;
while(iter) {
g_free(iter->data);
iter = g_slist_next(iter);
}
} else {
processOpenFileResult(result);
}
free(result);
// Release filters
if (options->filters != NULL) {
int index = 0;
GtkFileFilter* thisFilter;
while(options->filters[index] != 0) {
thisFilter = options->filters[index];
g_object_unref(thisFilter);
index++;
}
freeFileFilterArray(options->filters);
}
gtk_widget_destroy(dlgWidget);
free(options->title);
}
GtkFileFilter* newFileFilter() {
GtkFileFilter* result = gtk_file_filter_new();
g_object_ref(result);
return result;
}
typedef struct RGBAOptions {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
void *webview;
} RGBAOptions;
void setBackgroundColour(void* data) {
RGBAOptions* options = (RGBAOptions*)data;
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
}
typedef struct SetTitleArgs {
GtkWindow* window;
char* title;
} SetTitleArgs;
gboolean setTitle(gpointer data) {
SetTitleArgs* args = (SetTitleArgs*)data;
gtk_window_set_title(args->window, args->title);
free((void*)args->title);
free((void*)data);
return G_SOURCE_REMOVE;
}
void SetTitle(GtkWindow* window, char* title) {
SetTitleArgs* args = malloc(sizeof(SetTitleArgs));
args->window = window;
args->title = title;
ExecuteOnMainThread(setTitle, (gpointer)args);
}
typedef struct SetPositionArgs {
int x;
int y;
void* window;
} SetPositionArgs;
gboolean setPosition(gpointer data) {
SetPositionArgs* args = (SetPositionArgs*)data;
gtk_window_move((GtkWindow*)args->window, args->x, args->y);
free(args);
return G_SOURCE_REMOVE;
}
void SetPosition(void* window, int x, int y) {
GdkRectangle monitorDimensions = getCurrentMonitorGeometry(window);
SetPositionArgs* args = malloc(sizeof(SetPositionArgs));
args->window = window;
args->x = monitorDimensions.x + x;
args->y = monitorDimensions.y + y;
ExecuteOnMainThread(setPosition, (gpointer)args);
}
gboolean Show(gpointer data) {
gtk_widget_show((GtkWidget*)data);
return G_SOURCE_REMOVE;
}
gboolean Hide(gpointer data) {
gtk_widget_hide((GtkWidget*)data);
return G_SOURCE_REMOVE;
}
gboolean Maximise(gpointer data) {
gtk_window_maximize((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean UnMaximise(gpointer data) {
gtk_window_unmaximize((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean Minimise(gpointer data) {
gtk_window_iconify((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean UnMinimise(gpointer data) {
gtk_window_present((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean Fullscreen(gpointer data) {
GtkWindow* window = (GtkWindow*)data;
// Get the geometry of the monitor.
GdkRectangle m = getCurrentMonitorGeometry(window);
int scale = getCurrentMonitorScaleFactor(window);
SetMinMaxSize(window, 0, 0, m.width * scale, m.height * scale);
gtk_window_fullscreen(window);
return G_SOURCE_REMOVE;
}
gboolean UnFullscreen(gpointer data) {
gtk_window_unfullscreen((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
bool disableContextMenu(GtkWindow* window) {
return TRUE;
}
void DisableContextMenu(void* webview) {
contextMenuDisabled = TRUE;
g_signal_connect(WEBKIT_WEB_VIEW(webview), "context-menu", G_CALLBACK(disableContextMenu), NULL);
}
void SetWindowIcon(GtkWindow* window, const guchar* buf, gsize len) {
GdkPixbufLoader* loader = gdk_pixbuf_loader_new();
if (!loader) {
return;
}
if (gdk_pixbuf_loader_write(loader, buf, len, NULL) && gdk_pixbuf_loader_close(loader, NULL)) {
GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
if (pixbuf) {
gtk_window_set_icon(window, pixbuf);
}
}
g_object_unref(loader);
}
*/
import "C"
import (
"strings"
"sync"
"unsafe"
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/options"
)
func gtkBool(input bool) C.gboolean {
if input {
return C.gboolean(1)
}
return C.gboolean(0)
}
type Window struct {
appoptions *options.App
debug bool
gtkWindow unsafe.Pointer
contentManager unsafe.Pointer
webview unsafe.Pointer
applicationMenu *menu.Menu
menubar *C.GtkWidget
vbox *C.GtkWidget
accels *C.GtkAccelGroup
minWidth, minHeight, maxWidth, maxHeight int
}
func bool2Cint(value bool) C.int {
if value {
return C.int(1)
}
return C.int(0)
}
func NewWindow(appoptions *options.App, debug bool) *Window {
result := &Window{
appoptions: appoptions,
debug: debug,
minHeight: appoptions.MinHeight,
minWidth: appoptions.MinWidth,
maxHeight: appoptions.MaxHeight,
maxWidth: appoptions.MaxWidth,
}
gtkWindow := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL)
C.g_object_ref_sink(C.gpointer(gtkWindow))
result.gtkWindow = unsafe.Pointer(gtkWindow)
result.vbox = C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0)
C.gtk_container_add(result.asGTKContainer(), result.vbox)
result.contentManager = unsafe.Pointer(C.webkit_user_content_manager_new())
external := C.CString("external")
defer C.free(unsafe.Pointer(external))
C.webkit_user_content_manager_register_script_message_handler(result.cWebKitUserContentManager(), external)
C.setupInvokeSignal(result.contentManager)
webview := C.setupWebview(result.contentManager, result.asGTKWindow(), bool2Cint(appoptions.HideWindowOnClose))
result.webview = unsafe.Pointer(webview)
buttonPressedName := C.CString("button-press-event")
defer C.free(unsafe.Pointer(buttonPressedName))
C.connectButtons(unsafe.Pointer(webview))
if debug {
C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1))
} else {
C.DisableContextMenu(unsafe.Pointer(webview))
}
// Set background colour
RGBA := appoptions.BackgroundColour
result.SetBackgroundColour(RGBA.R, RGBA.G, RGBA.B, RGBA.A)
// Setup window
result.SetKeepAbove(appoptions.AlwaysOnTop)
result.SetResizable(!appoptions.DisableResize)
result.SetSize(appoptions.Width, appoptions.Height)
result.SetDecorated(!appoptions.Frameless)
result.SetTitle(appoptions.Title)
result.SetMinSize(appoptions.MinWidth, appoptions.MinHeight)
result.SetMaxSize(appoptions.MaxWidth, appoptions.MaxHeight)
if appoptions.Linux != nil {
if appoptions.Linux.Icon != nil {
result.SetWindowIcon(appoptions.Linux.Icon)
}
}
// Menu
result.SetApplicationMenu(appoptions.Menu)
return result
}
func (w *Window) asGTKWidget() *C.GtkWidget {
return C.GTKWIDGET(w.gtkWindow)
}
func (w *Window) asGTKWindow() *C.GtkWindow {
return C.GTKWINDOW(w.gtkWindow)
}
func (w *Window) asGTKContainer() *C.GtkContainer {
return C.GTKCONTAINER(w.gtkWindow)
}
func (w *Window) cWebKitUserContentManager() *C.WebKitUserContentManager {
return (*C.WebKitUserContentManager)(w.contentManager)
}
func (w *Window) Fullscreen() {
C.ExecuteOnMainThread(C.Fullscreen, C.gpointer(w.asGTKWindow()))
}
func (w *Window) UnFullscreen() {
if !w.IsFullScreen() {
return
}
C.ExecuteOnMainThread(C.UnFullscreen, C.gpointer(w.asGTKWindow()))
w.SetMinSize(w.minWidth, w.minHeight)
w.SetMaxSize(w.maxWidth, w.maxHeight)
}
func (w *Window) Destroy() {
C.gtk_widget_destroy(w.asGTKWidget())
C.g_object_unref(C.gpointer(w.gtkWindow))
}
func (w *Window) Close() {
C.gtk_window_close(w.asGTKWindow())
}
func (w *Window) Center() {
C.ExecuteOnMainThread(C.Center, C.gpointer(w.asGTKWindow()))
}
func (w *Window) SetPosition(x int, y int) {
C.SetPosition(unsafe.Pointer(w.asGTKWindow()), C.int(x), C.int(y))
}
func (w *Window) Size() (int, int) {
var width, height C.int
var wg sync.WaitGroup
wg.Add(1)
invokeOnMainThread(func() {
C.gtk_window_get_size(w.asGTKWindow(), &width, &height)
wg.Done()
})
wg.Wait()
return int(width), int(height)
}
func (w *Window) GetPosition() (int, int) {
var width, height C.int
var wg sync.WaitGroup
wg.Add(1)
invokeOnMainThread(func() {
C.gtk_window_get_position(w.asGTKWindow(), &width, &height)
wg.Done()
})
wg.Wait()
return int(width), int(height)
}
func (w *Window) SetMaxSize(maxWidth int, maxHeight int) {
w.maxHeight = maxHeight
w.maxWidth = maxWidth
C.SetMinMaxSize(w.asGTKWindow(), C.int(w.minWidth), C.int(w.minHeight), C.int(w.maxWidth), C.int(w.maxHeight))
}
func (w *Window) SetMinSize(minWidth int, minHeight int) {
w.minHeight = minHeight
w.minWidth = minWidth
C.SetMinMaxSize(w.asGTKWindow(), C.int(w.minWidth), C.int(w.minHeight), C.int(w.maxWidth), C.int(w.maxHeight))
}
func (w *Window) Show() {
C.ExecuteOnMainThread(C.Show, C.gpointer(w.asGTKWindow()))
}
func (w *Window) Hide() {
C.ExecuteOnMainThread(C.Hide, C.gpointer(w.asGTKWindow()))
}
func (w *Window) Maximise() {
C.ExecuteOnMainThread(C.Maximise, C.gpointer(w.asGTKWindow()))
}
func (w *Window) UnMaximise() {
C.ExecuteOnMainThread(C.UnMaximise, C.gpointer(w.asGTKWindow()))
}
func (w *Window) Minimise() {
C.ExecuteOnMainThread(C.Minimise, C.gpointer(w.asGTKWindow()))
}
func (w *Window) UnMinimise() {
C.ExecuteOnMainThread(C.UnMinimise, C.gpointer(w.asGTKWindow()))
}
func (w *Window) IsFullScreen() bool {
result := C.IsFullscreen(w.asGTKWidget())
if result != 0 {
return true
}
return false
}
func (w *Window) IsMaximised() bool {
result := C.IsMaximised(w.asGTKWidget())
return result > 0
}
func (w *Window) SetBackgroundColour(r uint8, g uint8, b uint8, a uint8) {
data := C.RGBAOptions{
r: C.uchar(r),
g: C.uchar(g),
b: C.uchar(b),
a: C.uchar(a),
webview: w.webview,
}
invokeOnMainThread(func() { C.setBackgroundColour(unsafe.Pointer(&data)) })
}
func (w *Window) SetWindowIcon(icon []byte) {
if len(icon) == 0 {
return
}
C.SetWindowIcon(w.asGTKWindow(), (*C.guchar)(&icon[0]), (C.gsize)(len(icon)))
}
func (w *Window) Run(url string) {
if w.menubar != nil {
C.gtk_box_pack_start(C.GTKBOX(unsafe.Pointer(w.vbox)), w.menubar, 0, 0, 0)
}
C.gtk_box_pack_start(C.GTKBOX(unsafe.Pointer(w.vbox)), C.GTKWIDGET(w.webview), 1, 1, 0)
_url := C.CString(url)
C.loadIndex(w.webview, _url)
defer C.free(unsafe.Pointer(_url))
C.gtk_widget_show_all(w.asGTKWidget())
w.Center()
switch w.appoptions.WindowStartState {
case options.Fullscreen:
w.Fullscreen()
case options.Minimised:
w.Minimise()
case options.Maximised:
w.Maximise()
}
C.gtk_main()
w.Destroy()
}
func (w *Window) SetKeepAbove(top bool) {
C.gtk_window_set_keep_above(w.asGTKWindow(), gtkBool(top))
}
func (w *Window) SetResizable(resizable bool) {
C.gtk_window_set_resizable(w.asGTKWindow(), gtkBool(resizable))
}
func (w *Window) SetSize(width int, height int) {
C.gtk_window_resize(w.asGTKWindow(), C.gint(width), C.gint(height))
}
func (w *Window) SetDecorated(frameless bool) {
C.gtk_window_set_decorated(w.asGTKWindow(), gtkBool(frameless))
}
func (w *Window) SetTitle(title string) {
C.SetTitle(w.asGTKWindow(), C.CString(title))
}
func (w *Window) ExecJS(js string) {
jscallback := C.JSCallback{
webview: w.webview,
script: C.CString(js),
}
invokeOnMainThread(func() { C.executeJS(unsafe.Pointer(&jscallback)) })
}
func (w *Window) StartDrag() {
C.StartDrag(w.webview, w.asGTKWindow())
}
func (w *Window) Quit() {
C.gtk_main_quit()
}
func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multipleFiles int, action C.GtkFileChooserAction) {
data := C.OpenFileDialogOptions{
window: w.asGTKWindow(),
title: C.CString(dialogOptions.Title),
multipleFiles: C.int(multipleFiles),
action: action,
}
if len(dialogOptions.Filters) > 0 {
// Create filter array
mem := NewCalloc()
arraySize := len(dialogOptions.Filters) + 1
data.filters = C.allocFileFilterArray((C.size_t)(arraySize))
filters := unsafe.Slice((**C.struct__GtkFileFilter)(unsafe.Pointer(data.filters)), arraySize)
for index, filter := range dialogOptions.Filters {
thisFilter := C.gtk_file_filter_new()
C.g_object_ref(C.gpointer(thisFilter))
if filter.DisplayName != "" {
cName := mem.String(filter.DisplayName)
C.gtk_file_filter_set_name(thisFilter, cName)
}
if filter.Pattern != "" {
for _, thisPattern := range strings.Split(filter.Pattern, ";") {
cThisPattern := mem.String(thisPattern)
C.gtk_file_filter_add_pattern(thisFilter, cThisPattern)
}
}
// Add filter to array
filters[index] = thisFilter
}
mem.Free()
filters[arraySize-1] = nil
}
if dialogOptions.CanCreateDirectories {
data.createDirectories = C.int(1)
}
if dialogOptions.ShowHiddenFiles {
data.showHiddenFiles = C.int(1)
}
if dialogOptions.DefaultFilename != "" {
data.defaultFilename = C.CString(dialogOptions.DefaultFilename)
}
if dialogOptions.DefaultDirectory != "" {
data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory)
}
invokeOnMainThread(func() { C.opendialog(unsafe.Pointer(&data)) })
}
func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
data := C.MessageDialogOptions{
window: w.gtkWindow,
title: C.CString(dialogOptions.Title),
message: C.CString(dialogOptions.Message),
}
switch dialogOptions.Type {
case frontend.InfoDialog:
data.messageType = C.int(0)
case frontend.ErrorDialog:
data.messageType = C.int(1)
case frontend.QuestionDialog:
data.messageType = C.int(2)
case frontend.WarningDialog:
data.messageType = C.int(3)
}
invokeOnMainThread(func() { C.messageDialog(unsafe.Pointer(&data)) })
}
func (w *Window) ToggleMaximise() {
if w.IsMaximised() {
w.UnMaximise()
} else {
w.Maximise()
}
}