mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
[v3 linux] initial linux implementation
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// FIXME: This should be handled appropriately in the individual files most likely.
|
||||
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
|
||||
_ = os.Setenv("GDK_BACKEND", "x11")
|
||||
}
|
||||
|
||||
type linuxApp struct {
|
||||
application pointer
|
||||
applicationMenu pointer
|
||||
parent *App
|
||||
|
||||
startupActions []func()
|
||||
|
||||
// Native -> uint
|
||||
windows map[windowPointer]uint
|
||||
windowsLock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *linuxApp) GetFlags(options Options) map[string]any {
|
||||
if options.Flags == nil {
|
||||
options.Flags = make(map[string]any)
|
||||
}
|
||||
return options.Flags
|
||||
}
|
||||
|
||||
func getNativeApplication() *linuxApp {
|
||||
return globalApplication.impl.(*linuxApp)
|
||||
}
|
||||
|
||||
func (m *linuxApp) hide() {
|
||||
hideAllWindows(m.application)
|
||||
}
|
||||
|
||||
func (m *linuxApp) show() {
|
||||
showAllWindows(m.application)
|
||||
}
|
||||
|
||||
func (m *linuxApp) on(eventID uint) {
|
||||
// TODO: What do we need to do here?
|
||||
log.Println("linuxApp.on()", eventID)
|
||||
}
|
||||
|
||||
func (m *linuxApp) setIcon(icon []byte) {
|
||||
fmt.Println("linuxApp.setIcon", "not implemented")
|
||||
}
|
||||
|
||||
func (m *linuxApp) name() string {
|
||||
return appName()
|
||||
}
|
||||
|
||||
func (m *linuxApp) getCurrentWindowID() uint {
|
||||
return getCurrentWindowID(m.application, m.windows)
|
||||
}
|
||||
|
||||
func (m *linuxApp) setApplicationMenu(menu *Menu) {
|
||||
if menu == nil {
|
||||
// Create a default menu
|
||||
menu = defaultApplicationMenu()
|
||||
}
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
fmt.Println("setApplicationMenu")
|
||||
|
||||
menu.Update()
|
||||
m.applicationMenu = (menu.impl).(*linuxMenu).native
|
||||
})
|
||||
}
|
||||
|
||||
func (m *linuxApp) run() error {
|
||||
|
||||
// Add a hook to the ApplicationDidFinishLaunching event
|
||||
// FIXME: add Wails specific events - i.e. Shouldn't platform specific ones be translated to Wails events?
|
||||
m.parent.On(events.Mac.ApplicationDidFinishLaunching, func() {
|
||||
// Do we need to do anything now?
|
||||
fmt.Println("events.Mac.ApplicationDidFinishLaunching received!")
|
||||
})
|
||||
|
||||
return appRun(m.application)
|
||||
}
|
||||
|
||||
func (m *linuxApp) destroy() {
|
||||
appDestroy(m.application)
|
||||
}
|
||||
|
||||
// register our window to our parent mapping
|
||||
func (m *linuxApp) registerWindow(window pointer, id uint) {
|
||||
m.windowsLock.Lock()
|
||||
m.windows[windowPointer(window)] = id
|
||||
m.windowsLock.Unlock()
|
||||
}
|
||||
|
||||
func newPlatformApp(parent *App) *linuxApp {
|
||||
name := strings.ToLower(strings.Replace(parent.options.Name, " ", "", -1))
|
||||
if name == "" {
|
||||
name = "undefined"
|
||||
}
|
||||
app := &linuxApp{
|
||||
parent: parent,
|
||||
application: appNew(name),
|
||||
windows: map[windowPointer]uint{},
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
/*
|
||||
//export processApplicationEvent
|
||||
func processApplicationEvent(eventID C.uint) {
|
||||
// TODO: add translation to Wails events
|
||||
// currently reusing Mac specific values
|
||||
applicationEvents <- uint(eventID)
|
||||
}
|
||||
|
||||
//export processWindowEvent
|
||||
func processWindowEvent(windowID C.uint, eventID C.uint) {
|
||||
windowEvents <- &WindowEvent{
|
||||
WindowID: uint(windowID),
|
||||
EventID: uint(eventID),
|
||||
}
|
||||
}
|
||||
|
||||
//export processMessage
|
||||
func processMessage(windowID C.uint, message *C.char) {
|
||||
windowMessageBuffer <- &windowMessage{
|
||||
windowId: uint(windowID),
|
||||
message: C.GoString(message),
|
||||
}
|
||||
}
|
||||
|
||||
//export processDragItems
|
||||
func processDragItems(windowID C.uint, arr **C.char, length C.int) {
|
||||
var filenames []string
|
||||
// Convert the C array to a Go slice
|
||||
goSlice := (*[1 << 30]*C.char)(unsafe.Pointer(arr))[:length:length]
|
||||
for _, str := range goSlice {
|
||||
filenames = append(filenames, C.GoString(str))
|
||||
}
|
||||
windowDragAndDropBuffer <- &dragAndDropMessage{
|
||||
windowId: uint(windowID),
|
||||
filenames: filenames,
|
||||
}
|
||||
}
|
||||
|
||||
//export processMenuItemClick
|
||||
func processMenuItemClick(menuID identifier) {
|
||||
menuItemClicked <- uint(menuID)
|
||||
}
|
||||
|
||||
func setIcon(icon []byte) {
|
||||
if icon == nil {
|
||||
return
|
||||
}
|
||||
//C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon)))
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,33 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var clipboardLock sync.RWMutex
|
||||
|
||||
type linuxClipboard struct{}
|
||||
|
||||
func (m linuxClipboard) setText(text string) bool {
|
||||
clipboardLock.Lock()
|
||||
defer clipboardLock.Unlock()
|
||||
// cText := C.CString(text)
|
||||
// success := C.setClipboardText(cText)
|
||||
// C.free(unsafe.Pointer(cText))
|
||||
success := false
|
||||
return bool(success)
|
||||
}
|
||||
|
||||
func (m linuxClipboard) text() (string, bool) {
|
||||
clipboardLock.RLock()
|
||||
defer clipboardLock.RUnlock()
|
||||
// clipboardText := C.getClipboardText()
|
||||
// result := C.GoString(clipboardText)
|
||||
return "", false
|
||||
}
|
||||
|
||||
func newClipboardImpl() *linuxClipboard {
|
||||
return &linuxClipboard{}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package application
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (m *linuxApp) showAboutDialog(title string, message string, icon []byte) {
|
||||
window := globalApplication.getWindowForID(m.getCurrentWindowID())
|
||||
var parent pointer
|
||||
if window != nil {
|
||||
parent = window.impl.(*linuxWebviewWindow).window
|
||||
}
|
||||
about := newMessageDialog(InfoDialog)
|
||||
about.SetTitle(title).
|
||||
SetMessage(message).
|
||||
SetIcon(icon)
|
||||
runQuestionDialog(
|
||||
parent,
|
||||
about,
|
||||
)
|
||||
}
|
||||
|
||||
type linuxDialog struct {
|
||||
dialog *MessageDialog
|
||||
}
|
||||
|
||||
func (m *linuxDialog) show() {
|
||||
windowId := getNativeApplication().getCurrentWindowID()
|
||||
window := globalApplication.getWindowForID(windowId)
|
||||
var parent pointer
|
||||
if window != nil {
|
||||
parent = window.impl.(*linuxWebviewWindow).window
|
||||
}
|
||||
|
||||
response := runQuestionDialog(parent, m.dialog)
|
||||
if response >= 0 {
|
||||
fmt.Println("Response: ", response)
|
||||
button := m.dialog.Buttons[response]
|
||||
if button.Callback != nil {
|
||||
go button.Callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newDialogImpl(d *MessageDialog) *linuxDialog {
|
||||
return &linuxDialog{
|
||||
dialog: d,
|
||||
}
|
||||
}
|
||||
|
||||
type linuxOpenFileDialog struct {
|
||||
dialog *OpenFileDialog
|
||||
}
|
||||
|
||||
func newOpenFileDialogImpl(d *OpenFileDialog) *linuxOpenFileDialog {
|
||||
return &linuxOpenFileDialog{
|
||||
dialog: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *linuxOpenFileDialog) show() ([]string, error) {
|
||||
openFileResponses[m.dialog.id] = make(chan string)
|
||||
// nsWindow := unsafe.Pointer(nil)
|
||||
if m.dialog.window != nil {
|
||||
// get NSWindow from window
|
||||
//nsWindow = m.dialog.window.impl.(*macosWebviewWindow).nsWindow
|
||||
}
|
||||
|
||||
// Massage filter patterns into macOS format
|
||||
// We iterate all filter patterns, tidy them up and then join them with a semicolon
|
||||
// This should produce a single string of extensions like "png;jpg;gif"
|
||||
// var filterPatterns string
|
||||
// if len(m.dialog.filters) > 0 {
|
||||
// var allPatterns []string
|
||||
// for _, filter := range m.dialog.filters {
|
||||
// patternComponents := strings.Split(filter.Pattern, ";")
|
||||
// for i, component := range patternComponents {
|
||||
// filterPattern := strings.TrimSpace(component)
|
||||
// filterPattern = strings.TrimPrefix(filterPattern, "*.")
|
||||
// patternComponents[i] = filterPattern
|
||||
// }
|
||||
// allPatterns = append(allPatterns, strings.Join(patternComponents, ";"))
|
||||
// }
|
||||
// filterPatterns = strings.Join(allPatterns, ";")
|
||||
// }
|
||||
|
||||
// C.showOpenFileDialog(C.uint(m.dialog.id),
|
||||
// C.bool(m.dialog.canChooseFiles),
|
||||
// C.bool(m.dialog.canChooseDirectories),
|
||||
// C.bool(m.dialog.canCreateDirectories),
|
||||
// C.bool(m.dialog.showHiddenFiles),
|
||||
// C.bool(m.dialog.allowsMultipleSelection),
|
||||
// C.bool(m.dialog.resolvesAliases),
|
||||
// C.bool(m.dialog.hideExtension),
|
||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
||||
// C.bool(m.dialog.allowsOtherFileTypes),
|
||||
// toCString(filterPatterns),
|
||||
// C.uint(len(filterPatterns)),
|
||||
// toCString(m.dialog.message),
|
||||
// toCString(m.dialog.directory),
|
||||
// toCString(m.dialog.buttonText),
|
||||
// nsWindow)
|
||||
var result []string
|
||||
for filename := range openFileResponses[m.dialog.id] {
|
||||
result = append(result, filename)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type linuxSaveFileDialog struct {
|
||||
dialog *SaveFileDialog
|
||||
}
|
||||
|
||||
func newSaveFileDialogImpl(d *SaveFileDialog) *linuxSaveFileDialog {
|
||||
return &linuxSaveFileDialog{
|
||||
dialog: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *linuxSaveFileDialog) show() (string, error) {
|
||||
saveFileResponses[m.dialog.id] = make(chan string)
|
||||
// nsWindow := unsafe.Pointer(nil)
|
||||
if m.dialog.window != nil {
|
||||
// get NSWindow from window
|
||||
// nsWindow = m.dialog.window.impl.(*linuxWebviewWindow).nsWindow
|
||||
}
|
||||
|
||||
// C.showSaveFileDialog(C.uint(m.dialog.id),
|
||||
// C.bool(m.dialog.canCreateDirectories),
|
||||
// C.bool(m.dialog.showHiddenFiles),
|
||||
// C.bool(m.dialog.canSelectHiddenExtension),
|
||||
// C.bool(m.dialog.hideExtension),
|
||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
||||
// C.bool(m.dialog.allowOtherFileTypes),
|
||||
// toCString(m.dialog.message),
|
||||
// toCString(m.dialog.directory),
|
||||
// toCString(m.dialog.buttonText),
|
||||
// toCString(m.dialog.filename),
|
||||
// nsWindow)
|
||||
return <-saveFileResponses[m.dialog.id], nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
func (m *linuxApp) dispatchOnMainThread(id uint) {
|
||||
dispatchOnMainThread(id)
|
||||
}
|
||||
|
||||
func executeOnMainThread(callbackID uint) {
|
||||
mainThreadFunctionStoreLock.RLock()
|
||||
fn := mainThreadFunctionStore[callbackID]
|
||||
if fn == nil {
|
||||
Fatal("dispatchCallback called with invalid id: %v", callbackID)
|
||||
}
|
||||
delete(mainThreadFunctionStore, callbackID)
|
||||
mainThreadFunctionStoreLock.RUnlock()
|
||||
fn()
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
type linuxMenu struct {
|
||||
menu *Menu
|
||||
native pointer
|
||||
}
|
||||
|
||||
func newMenuImpl(menu *Menu) *linuxMenu {
|
||||
result := &linuxMenu{
|
||||
menu: menu,
|
||||
native: menuBarNew(),
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *linuxMenu) update() {
|
||||
m.processMenu(m.menu)
|
||||
}
|
||||
|
||||
func (m *linuxMenu) processMenu(menu *Menu) {
|
||||
if menu.impl == nil {
|
||||
menu.impl = &linuxMenu{
|
||||
menu: menu,
|
||||
native: menuNew(),
|
||||
}
|
||||
}
|
||||
var currentRadioGroup GSListPointer
|
||||
|
||||
for _, item := range menu.items {
|
||||
// drop the group if we have run out of radio items
|
||||
if item.itemType != radio {
|
||||
currentRadioGroup = nilRadioGroup
|
||||
}
|
||||
|
||||
switch item.itemType {
|
||||
case submenu:
|
||||
menuItem := newMenuItemImpl(item)
|
||||
item.impl = menuItem
|
||||
m.processMenu(item.submenu)
|
||||
m.addSubMenuToItem(item.submenu, item)
|
||||
m.addMenuItem(menu, item)
|
||||
case text, checkbox:
|
||||
menuItem := newMenuItemImpl(item)
|
||||
item.impl = menuItem
|
||||
m.addMenuItem(menu, item)
|
||||
case radio:
|
||||
menuItem := newRadioItemImpl(item, currentRadioGroup)
|
||||
item.impl = menuItem
|
||||
m.addMenuItem(menu, item)
|
||||
currentRadioGroup = menuGetRadioGroup(menuItem)
|
||||
case separator:
|
||||
m.addMenuSeparator(menu)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, item := range menu.items {
|
||||
if item.callback != nil {
|
||||
m.attachHandler(item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (m *linuxMenu) attachHandler(item *MenuItem) {
|
||||
attachMenuHandler(item)
|
||||
}
|
||||
|
||||
func (m *linuxMenu) addSubMenuToItem(menu *Menu, item *MenuItem) {
|
||||
if menu.impl == nil {
|
||||
menu.impl = &linuxMenu{
|
||||
menu: menu,
|
||||
native: menuNew(),
|
||||
}
|
||||
}
|
||||
menuSetSubmenu(item, menu)
|
||||
}
|
||||
|
||||
func (m *linuxMenu) addMenuItem(parent *Menu, menu *MenuItem) {
|
||||
menuAppend(parent, menu)
|
||||
}
|
||||
|
||||
func (m *linuxMenu) addMenuSeparator(menu *Menu) {
|
||||
menuAddSeparator(menu)
|
||||
|
||||
}
|
||||
|
||||
func (m *linuxMenu) addServicesMenu(menu *Menu) {
|
||||
// FIXME: Should this be required?
|
||||
}
|
||||
|
||||
func (l *linuxMenu) createMenu(name string, items []*MenuItem) *Menu {
|
||||
impl := newMenuImpl(&Menu{label: name})
|
||||
menu := &Menu{
|
||||
label: name,
|
||||
items: items,
|
||||
impl: impl,
|
||||
}
|
||||
impl.menu = menu
|
||||
return menu
|
||||
}
|
||||
|
||||
func defaultApplicationMenu() *Menu {
|
||||
menu := NewMenu()
|
||||
menu.AddRole(AppMenu)
|
||||
menu.AddRole(FileMenu)
|
||||
menu.AddRole(EditMenu)
|
||||
menu.AddRole(ViewMenu)
|
||||
menu.AddRole(WindowMenu)
|
||||
menu.AddRole(HelpMenu)
|
||||
return menu
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type linuxMenuItem struct {
|
||||
menuItem *MenuItem
|
||||
native pointer
|
||||
handlerId uint
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setTooltip(tooltip string) {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
menuItemSetToolTip(l.native, tooltip)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) blockSignal() {
|
||||
if l.handlerId != 0 {
|
||||
menuItemSignalBlock(l.native, l.handlerId, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) unBlockSignal() {
|
||||
if l.handlerId != 0 {
|
||||
menuItemSignalBlock(l.native, l.handlerId, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setLabel(s string) {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
menuItemSetLabel(l.native, s)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) isChecked() bool {
|
||||
return menuItemChecked(l.native)
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setDisabled(disabled bool) {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
menuItemSetDisabled(l.native, disabled)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setChecked(checked bool) {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
menuItemSetChecked(l.native, checked)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setHidden(hidden bool) {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
widgetSetVisible(l.native, hidden)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) setAccelerator(accelerator *accelerator) {
|
||||
fmt.Println("setAccelerator", accelerator)
|
||||
// Set the keyboard shortcut of the menu item
|
||||
// var modifier C.int
|
||||
// var key *C.char
|
||||
if accelerator != nil {
|
||||
// modifier = C.int(toMacModifier(accelerator.Modifiers))
|
||||
// key = C.CString(accelerator.Key)
|
||||
}
|
||||
|
||||
// Convert the key to a string
|
||||
// C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier)
|
||||
}
|
||||
|
||||
func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
|
||||
result := &linuxMenuItem{
|
||||
menuItem: item,
|
||||
}
|
||||
switch item.itemType {
|
||||
case text:
|
||||
result.native = menuItemNew(item.label)
|
||||
|
||||
case checkbox:
|
||||
result.native = menuCheckItemNew(item.label)
|
||||
result.setChecked(item.checked)
|
||||
if item.accelerator != nil {
|
||||
result.setAccelerator(item.accelerator)
|
||||
}
|
||||
case submenu:
|
||||
result.native = menuItemNew(item.label)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown menu type: %v", item.itemType))
|
||||
}
|
||||
result.setDisabled(result.menuItem.disabled)
|
||||
return result
|
||||
}
|
||||
|
||||
func newRadioItemImpl(item *MenuItem, group GSListPointer) *linuxMenuItem {
|
||||
result := &linuxMenuItem{
|
||||
menuItem: item,
|
||||
native: menuRadioItemNew(group, item.label),
|
||||
}
|
||||
result.setChecked(item.checked)
|
||||
result.setDisabled(result.menuItem.disabled)
|
||||
return result
|
||||
}
|
||||
|
||||
func newSpeechMenu() *MenuItem {
|
||||
speechMenu := NewMenu()
|
||||
speechMenu.Add("Start Speaking").
|
||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+.").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.startSpeaking()
|
||||
})
|
||||
speechMenu.Add("Stop Speaking").
|
||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+,").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.stopSpeaking()
|
||||
})
|
||||
subMenu := newSubMenuItem("Speech")
|
||||
subMenu.submenu = speechMenu
|
||||
return subMenu
|
||||
}
|
||||
|
||||
func newHideMenuItem() *MenuItem {
|
||||
return newMenuItem("Hide " + globalApplication.options.Name).
|
||||
SetAccelerator("CmdOrCtrl+h").
|
||||
OnClick(func(ctx *Context) {
|
||||
|
||||
// C.hideApplication()
|
||||
})
|
||||
}
|
||||
|
||||
func newHideOthersMenuItem() *MenuItem {
|
||||
return newMenuItem("Hide Others").
|
||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+h").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.hideOthers()
|
||||
})
|
||||
}
|
||||
|
||||
func newUnhideMenuItem() *MenuItem {
|
||||
return newMenuItem("Show All").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.showAll()
|
||||
})
|
||||
}
|
||||
|
||||
func newUndoMenuItem() *MenuItem {
|
||||
return newMenuItem("Undo").
|
||||
SetAccelerator("CmdOrCtrl+z").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.undo()
|
||||
})
|
||||
}
|
||||
|
||||
// newRedoMenuItem creates a new menu item for redoing the last action
|
||||
func newRedoMenuItem() *MenuItem {
|
||||
return newMenuItem("Redo").
|
||||
SetAccelerator("CmdOrCtrl+Shift+z").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.redo()
|
||||
})
|
||||
}
|
||||
|
||||
func newCutMenuItem() *MenuItem {
|
||||
return newMenuItem("Cut").
|
||||
SetAccelerator("CmdOrCtrl+x").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.cut()
|
||||
})
|
||||
}
|
||||
|
||||
func newCopyMenuItem() *MenuItem {
|
||||
return newMenuItem("Copy").
|
||||
SetAccelerator("CmdOrCtrl+c").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.copy()
|
||||
})
|
||||
}
|
||||
|
||||
func newPasteMenuItem() *MenuItem {
|
||||
return newMenuItem("Paste").
|
||||
SetAccelerator("CmdOrCtrl+v").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.paste()
|
||||
})
|
||||
}
|
||||
|
||||
func newPasteAndMatchStyleMenuItem() *MenuItem {
|
||||
return newMenuItem("Paste and Match Style").
|
||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+v").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.pasteAndMatchStyle()
|
||||
})
|
||||
}
|
||||
|
||||
func newDeleteMenuItem() *MenuItem {
|
||||
return newMenuItem("Delete").
|
||||
SetAccelerator("backspace").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.delete()
|
||||
})
|
||||
}
|
||||
|
||||
func newQuitMenuItem() *MenuItem {
|
||||
return newMenuItem("Quit " + globalApplication.options.Name).
|
||||
SetAccelerator("CmdOrCtrl+q").
|
||||
OnClick(func(ctx *Context) {
|
||||
globalApplication.Quit()
|
||||
})
|
||||
}
|
||||
|
||||
func newSelectAllMenuItem() *MenuItem {
|
||||
return newMenuItem("Select All").
|
||||
SetAccelerator("CmdOrCtrl+a").
|
||||
OnClick(func(ctx *Context) {
|
||||
// C.selectAll()
|
||||
})
|
||||
}
|
||||
|
||||
func newAboutMenuItem() *MenuItem {
|
||||
return newMenuItem("About " + globalApplication.options.Name).
|
||||
OnClick(func(ctx *Context) {
|
||||
globalApplication.ShowAboutDialog()
|
||||
})
|
||||
}
|
||||
|
||||
func newCloseMenuItem() *MenuItem {
|
||||
return newMenuItem("Close").
|
||||
SetAccelerator("CmdOrCtrl+w").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newReloadMenuItem() *MenuItem {
|
||||
return newMenuItem("Reload").
|
||||
SetAccelerator("CmdOrCtrl+r").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.Reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newForceReloadMenuItem() *MenuItem {
|
||||
return newMenuItem("Force Reload").
|
||||
SetAccelerator("CmdOrCtrl+Shift+r").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ForceReload()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newToggleFullscreenMenuItem() *MenuItem {
|
||||
result := newMenuItem("Toggle Full Screen").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ToggleFullscreen()
|
||||
}
|
||||
})
|
||||
if runtime.GOOS == "darwin" {
|
||||
result.SetAccelerator("Ctrl+Command+F")
|
||||
} else {
|
||||
result.SetAccelerator("F11")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newToggleDevToolsMenuItem() *MenuItem {
|
||||
return newMenuItem("Toggle Developer Tools").
|
||||
SetAccelerator("Alt+Command+I").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ToggleDevTools()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newZoomResetMenuItem() *MenuItem {
|
||||
// reset zoom menu item
|
||||
return newMenuItem("Actual Size").
|
||||
SetAccelerator("CmdOrCtrl+0").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ZoomReset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newZoomInMenuItem() *MenuItem {
|
||||
return newMenuItem("Zoom In").
|
||||
SetAccelerator("CmdOrCtrl+plus").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ZoomIn()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newZoomOutMenuItem() *MenuItem {
|
||||
return newMenuItem("Zoom Out").
|
||||
SetAccelerator("CmdOrCtrl+-").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ZoomOut()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newMinimizeMenuItem() *MenuItem {
|
||||
return newMenuItem("Minimize").
|
||||
SetAccelerator("CmdOrCtrl+M").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.Minimise()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newZoomMenuItem() *MenuItem {
|
||||
return newMenuItem("Zoom").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.Zoom()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newFullScreenMenuItem() *MenuItem {
|
||||
return newMenuItem("Fullscreen").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.Fullscreen()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package application
|
||||
|
||||
// LinuxWindow contains macOS specific options
|
||||
type LinuxWindow struct {
|
||||
ShowApplicationMenu bool
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (m *linuxApp) getPrimaryScreen() (*Screen, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (m *linuxApp) getScreens() ([]*Screen, error) {
|
||||
var wg sync.WaitGroup
|
||||
var screens []*Screen
|
||||
var err error
|
||||
wg.Add(1)
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
screens, err = getScreens(m.application)
|
||||
wg.Done()
|
||||
})
|
||||
wg.Wait()
|
||||
return screens, err
|
||||
}
|
||||
|
||||
func getScreenForWindow(window *linuxWebviewWindow) (*Screen, error) {
|
||||
return window.getScreen()
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
type linuxSystemTray struct {
|
||||
id uint
|
||||
label string
|
||||
icon []byte
|
||||
menu *Menu
|
||||
|
||||
iconPosition int
|
||||
isTemplateIcon bool
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setIconPosition(position int) {
|
||||
s.iconPosition = position
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setMenu(menu *Menu) {
|
||||
s.menu = menu
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) run() {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
// if s.nsStatusItem != nil {
|
||||
// Fatal("System tray '%d' already running", s.id)
|
||||
// }
|
||||
// s.nsStatusItem = unsafe.Pointer(C.systemTrayNew())
|
||||
if s.label != "" {
|
||||
// C.systemTraySetLabel(s.nsStatusItem, C.CString(s.label))
|
||||
}
|
||||
if s.icon != nil {
|
||||
// s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&s.icon[0]), C.int(len(s.icon))))
|
||||
// C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
|
||||
}
|
||||
if s.menu != nil {
|
||||
s.menu.Update()
|
||||
// Convert impl to macosMenu object
|
||||
// s.nsMenu = (s.menu.impl).(*macosMenu).nsMenu
|
||||
// C.systemTraySetMenu(s.nsStatusItem, s.nsMenu)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setIcon(icon []byte) {
|
||||
s.icon = icon
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
// s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon))))
|
||||
// C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setDarkModeIcon(icon []byte) {
|
||||
s.icon = icon
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
// s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon))))
|
||||
// C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setTemplateIcon(icon []byte) {
|
||||
s.icon = icon
|
||||
s.isTemplateIcon = true
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
// s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon))))
|
||||
// C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
|
||||
})
|
||||
}
|
||||
|
||||
func newSystemTrayImpl(s *SystemTray) systemTrayImpl {
|
||||
return &linuxSystemTray{
|
||||
id: s.id,
|
||||
label: s.label,
|
||||
icon: s.icon,
|
||||
menu: s.menu,
|
||||
iconPosition: s.iconPosition,
|
||||
isTemplateIcon: s.isTemplateIcon,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) setLabel(label string) {
|
||||
s.label = label
|
||||
// C.systemTraySetLabel(s.nsStatusItem, C.CString(label))
|
||||
}
|
||||
|
||||
func (s *linuxSystemTray) destroy() {
|
||||
// Remove the status item from the status bar and its associated menu
|
||||
// C.systemTrayDestroy(s.nsStatusItem)
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
//go:build linux
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
var showDevTools = func(window unsafe.Pointer) {}
|
||||
|
||||
type dragInfo struct {
|
||||
XRoot int
|
||||
YRoot int
|
||||
DragTime int
|
||||
MouseButton uint
|
||||
}
|
||||
|
||||
type linuxWebviewWindow struct {
|
||||
id uint
|
||||
application pointer
|
||||
window pointer
|
||||
webview pointer
|
||||
parent *WebviewWindow
|
||||
menubar pointer
|
||||
vbox pointer
|
||||
menu *menu.Menu
|
||||
accels pointer
|
||||
lastWidth int
|
||||
lastHeight int
|
||||
drag dragInfo
|
||||
}
|
||||
|
||||
var (
|
||||
registered bool = false // avoid 'already registered message' about 'wails://'
|
||||
)
|
||||
|
||||
func (w *linuxWebviewWindow) startDrag() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) endDrag(button uint, x, y int) {
|
||||
fmt.Println("endDrag", button, x, y)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) enableDND() {
|
||||
windowEnableDND(w.parent.id, w.webview)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) connectSignals() {
|
||||
windowSetupSignalHandlers(w.parent.id, w.window, w.webview, w.parent.options.HideOnClose)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData) {
|
||||
// Create the menu
|
||||
thisMenu := newMenuImpl(menu)
|
||||
thisMenu.update()
|
||||
fmt.Println("linux.openContextMenu() - not implemented")
|
||||
/* void
|
||||
gtk_menu_popup_at_rect (
|
||||
GtkMenu* menu,
|
||||
GdkWindow* rect_window,
|
||||
const GdkRectangle* rect,
|
||||
GdkGravity rect_anchor,
|
||||
GdkGravity menu_anchor,
|
||||
const GdkEvent* trigger_event
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) getZoom() float64 {
|
||||
return windowZoom(w.webview)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setZoom(zoom float64) {
|
||||
windowZoomSet(w.webview, zoom)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setFrameless(frameless bool) {
|
||||
windowSetFrameless(w.window, frameless)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) getScreen() (*Screen, error) {
|
||||
mx, my, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||
return &Screen{
|
||||
ID: fmt.Sprintf("%d", w.id), // A unique identifier for the display
|
||||
Name: w.parent.Name(), // The name of the display
|
||||
Scale: float32(scale), // The scale factor of the display
|
||||
X: mx, // The x-coordinate of the top-left corner of the rectangle
|
||||
Y: my, // The y-coordinate of the top-left corner of the rectangle
|
||||
Size: Size{Width: width, Height: height}, // The size of the display
|
||||
Bounds: Rect{}, // The bounds of the display
|
||||
WorkArea: Rect{}, // The work area of the display
|
||||
IsPrimary: false, // Whether this is the primary display
|
||||
Rotation: 0.0, // The rotation of the display
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) focus() {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
windowPresent(w.window)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) show() {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
windowShow(w.window)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) hide() {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
windowHide(w.window)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) isNormal() bool {
|
||||
return !w.isMinimised() && !w.isMaximised() && !w.isFullscreen()
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) isVisible() bool {
|
||||
return windowIsVisible(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setFullscreenButtonEnabled(enabled bool) {
|
||||
// C.setFullscreenButtonEnabled(w.nsWindow, C.bool(enabled))
|
||||
fmt.Println("setFullscreenButtonEnabled - not implemented")
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) disableSizeConstraints() {
|
||||
x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||
w.setMinMaxSize(x, y, width*scale, height*scale)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) unfullscreen() {
|
||||
fmt.Println("unfullscreen")
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
windowUnfullscreen(w.window)
|
||||
w.unmaximise()
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) fullscreen() {
|
||||
w.maximise()
|
||||
w.lastWidth, w.lastHeight = w.size()
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
||||
return
|
||||
}
|
||||
w.setMinMaxSize(0, 0, width*scale, height*scale)
|
||||
w.setSize(width*scale, height*scale)
|
||||
windowFullscreen(w.window)
|
||||
w.setPosition(0, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) unminimise() {
|
||||
windowPresent(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) unmaximise() {
|
||||
windowUnmaximize(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) maximise() {
|
||||
windowMaximize(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) minimise() {
|
||||
windowMinimize(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) on(eventID uint) {
|
||||
// Don't think this is correct!
|
||||
// GTK Events are strings
|
||||
fmt.Println("on()", eventID)
|
||||
//C.registerListener(C.uint(eventID))
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) zoom() {
|
||||
w.zoomIn()
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) windowZoom() {
|
||||
w.zoom() // FIXME> This should be removed
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) close() {
|
||||
windowClose(w.window)
|
||||
if !w.parent.options.HideOnClose {
|
||||
globalApplication.deleteWindowByID(w.parent.id)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) zoomIn() {
|
||||
windowZoomIn(w.webview)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) zoomOut() {
|
||||
windowZoomOut(w.webview)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) zoomReset() {
|
||||
windowZoomSet(w.webview, 0.0)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) reload() {
|
||||
windowReload(w.webview, "wails://")
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) forceReload() {
|
||||
w.reload()
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) center() {
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
x, y, width, height, _ := windowGetCurrentMonitorGeometry(w.window)
|
||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
||||
return
|
||||
}
|
||||
windowWidth, windowHeight := windowGetSize(w.window)
|
||||
|
||||
newX := ((width - int(windowWidth)) / 2) + x
|
||||
newY := ((height - int(windowHeight)) / 2) + y
|
||||
|
||||
// Place the window at the center of the monitor
|
||||
windowMove(w.window, newX, newY)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) isMinimised() bool {
|
||||
return windowIsMinimized(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) isMaximised() bool {
|
||||
return w.syncMainThreadReturningBool(func() bool {
|
||||
return windowIsMaximized(w.window)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) isFullscreen() bool {
|
||||
return w.syncMainThreadReturningBool(func() bool {
|
||||
return windowIsFullscreen(w.window)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) syncMainThreadReturningBool(fn func() bool) bool {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var result bool
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
result = fn()
|
||||
wg.Done()
|
||||
})
|
||||
wg.Wait()
|
||||
return result
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) restore() {
|
||||
// restore window to normal size
|
||||
// FIXME: never called! - remove from webviewImpl interface
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) execJS(js string) {
|
||||
windowExecJS(w.webview, js)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setURL(uri string) {
|
||||
if uri != "" {
|
||||
url, err := url.Parse(uri)
|
||||
if err == nil && url.Scheme == "" && url.Host == "" {
|
||||
// TODO handle this in a central location, the scheme and host might be platform dependant.
|
||||
url.Scheme = "wails"
|
||||
url.Host = "wails"
|
||||
uri = url.String()
|
||||
}
|
||||
}
|
||||
windowSetURL(w.webview, uri)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {
|
||||
windowSetKeepAbove(w.window, alwaysOnTop)
|
||||
}
|
||||
|
||||
func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow {
|
||||
// (*C.struct__GtkWidget)(m.native)
|
||||
//var menubar *C.struct__GtkWidget
|
||||
return &linuxWebviewWindow{
|
||||
application: getNativeApplication().application,
|
||||
parent: parent,
|
||||
// menubar: menubar,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setTitle(title string) {
|
||||
if !w.parent.options.Frameless {
|
||||
windowSetTitle(w.window, title)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setSize(width, height int) {
|
||||
windowResize(w.window, width, height)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setMinMaxSize(minWidth, minHeight, maxWidth, maxHeight int) {
|
||||
if minWidth == 0 {
|
||||
minWidth = -1
|
||||
}
|
||||
if minHeight == 0 {
|
||||
minHeight = -1
|
||||
}
|
||||
if maxWidth == 0 {
|
||||
maxWidth = -1
|
||||
}
|
||||
if maxHeight == 0 {
|
||||
maxHeight = -1
|
||||
}
|
||||
windowSetGeometryHints(w.window, minWidth, minHeight, maxWidth, maxHeight)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setMinSize(width, height int) {
|
||||
w.setMinMaxSize(width, height, w.parent.options.MaxWidth, w.parent.options.MaxHeight)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setMaxSize(width, height int) {
|
||||
w.setMinMaxSize(w.parent.options.MinWidth, w.parent.options.MinHeight, width, height)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setResizable(resizable bool) {
|
||||
windowSetResizable(w.window, resizable)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) toggleDevTools() {
|
||||
windowToggleDevTools(w.webview)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) size() (int, int) {
|
||||
/* var width, height C.int
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
|
||||
C.gtk_window_get_size((*C.GtkWindow)(w.window), &width, &height)
|
||||
wg.Done()
|
||||
})
|
||||
wg.Wait()
|
||||
return int(width), int(height)
|
||||
*/
|
||||
// Does this need to be guarded?
|
||||
return windowGetSize(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setPosition(x, y int) {
|
||||
mx, my, _, _, _ := windowGetCurrentMonitorGeometry(w.window)
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
windowMove(w.window, x+mx, y+my)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) width() int {
|
||||
width, _ := w.size()
|
||||
return width
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) height() int {
|
||||
_, height := w.size()
|
||||
return height
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) run() {
|
||||
for eventId := range w.parent.eventListeners {
|
||||
w.on(eventId)
|
||||
}
|
||||
|
||||
app := getNativeApplication()
|
||||
menu := app.applicationMenu
|
||||
|
||||
globalApplication.dispatchOnMainThread(func() {
|
||||
w.window, w.webview = windowNew(app.application, menu, w.parent.id, 1)
|
||||
app.registerWindow(w.window, w.parent.id) // record our mapping
|
||||
w.connectSignals()
|
||||
if w.parent.options.EnableDragAndDrop {
|
||||
w.enableDND()
|
||||
}
|
||||
w.setTitle(w.parent.options.Title)
|
||||
w.setAlwaysOnTop(w.parent.options.AlwaysOnTop)
|
||||
w.setResizable(!w.parent.options.DisableResize)
|
||||
// only set min/max size if actually set
|
||||
if w.parent.options.MinWidth != 0 &&
|
||||
w.parent.options.MinHeight != 0 &&
|
||||
w.parent.options.MaxWidth != 0 &&
|
||||
w.parent.options.MaxHeight != 0 {
|
||||
w.setMinMaxSize(
|
||||
w.parent.options.MinWidth,
|
||||
w.parent.options.MinHeight,
|
||||
w.parent.options.MaxWidth,
|
||||
w.parent.options.MaxHeight,
|
||||
)
|
||||
}
|
||||
w.setSize(w.parent.options.Width, w.parent.options.Height)
|
||||
w.setZoom(w.parent.options.Zoom)
|
||||
w.setBackgroundColour(w.parent.options.BackgroundColour)
|
||||
w.setFrameless(w.parent.options.Frameless)
|
||||
|
||||
if w.parent.options.X != 0 || w.parent.options.Y != 0 {
|
||||
w.setPosition(w.parent.options.X, w.parent.options.Y)
|
||||
} else {
|
||||
fmt.Println("attempting to set in the center")
|
||||
w.center()
|
||||
}
|
||||
switch w.parent.options.StartState {
|
||||
case WindowStateMaximised:
|
||||
w.maximise()
|
||||
case WindowStateMinimised:
|
||||
w.minimise()
|
||||
case WindowStateFullscreen:
|
||||
w.fullscreen()
|
||||
}
|
||||
|
||||
if w.parent.options.URL != "" {
|
||||
w.setURL(w.parent.options.URL)
|
||||
}
|
||||
// We need to wait for the HTML to load before we can execute the javascript
|
||||
// FIXME: What event is this? DomReady?
|
||||
w.parent.On(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEventContext) {
|
||||
if w.parent.options.JS != "" {
|
||||
w.execJS(w.parent.options.JS)
|
||||
}
|
||||
if w.parent.options.CSS != "" {
|
||||
js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS)
|
||||
w.execJS(js)
|
||||
}
|
||||
})
|
||||
if w.parent.options.HTML != "" {
|
||||
w.setHTML(w.parent.options.HTML)
|
||||
}
|
||||
if !w.parent.options.Hidden {
|
||||
w.show()
|
||||
if w.parent.options.X != 0 || w.parent.options.Y != 0 {
|
||||
w.setPosition(w.parent.options.X, w.parent.options.Y)
|
||||
} else {
|
||||
w.center() // needs to be queued until after GTK starts up!
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setTransparent() {
|
||||
windowSetTransparent(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) {
|
||||
if colour.Alpha != 0 {
|
||||
w.setTransparent()
|
||||
}
|
||||
windowSetBackgroundColour(w.webview, colour)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) position() (int, int) {
|
||||
var x, y int
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go globalApplication.dispatchOnMainThread(func() {
|
||||
x, y = windowGetPosition(w.window)
|
||||
wg.Done()
|
||||
})
|
||||
wg.Wait()
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) destroy() {
|
||||
windowDestroy(w.window)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) setHTML(html string) {
|
||||
windowSetHTML(w.webview, html)
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) startResize(border string) error {
|
||||
// FIXME: what do we need to do here?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *linuxWebviewWindow) nativeWindowHandle() uintptr {
|
||||
return uintptr(w.window)
|
||||
}
|
||||
Reference in New Issue
Block a user