Support linux systrays, of sorts...

This commit is contained in:
Lea Anthony
2024-03-06 16:59:13 -06:00
committed by Travis McLane
parent 12e2e2101a
commit 161444107a
26 changed files with 662 additions and 503 deletions
+54 -4
View File
@@ -60,6 +60,7 @@ typedef struct WindowEvent {
void activateLinux(gpointer data);
extern void emit(WindowEvent* data);
extern gboolean handleDeleteEvent(GtkWidget*, GdkEvent*, uintptr_t);
extern gboolean handleFocusEvent(GtkWidget*, GdkEvent*, uintptr_t);
extern void handleLoadChanged(WebKitWebView*, WebKitLoadEvent, uintptr_t);
void handleClick(void*);
extern gboolean onButtonEvent(GtkWidget *widget, GdkEventButton *event, uintptr_t user_data);
@@ -701,6 +702,31 @@ func (w *linuxWebviewWindow) execJS(js string) {
C.free(unsafe.Pointer(value))
}
func getMousePosition() (int, int, *Screen) {
var x, y C.gint
var screen *C.GdkScreen
defaultDisplay := C.gdk_display_get_default()
device := C.gdk_seat_get_pointer(C.gdk_display_get_default_seat(defaultDisplay))
C.gdk_device_get_position(device, &screen, &x, &y)
// Get Monitor for screen
monitor := C.gdk_display_get_monitor_at_point(defaultDisplay, x, y)
geometry := C.GdkRectangle{}
C.gdk_monitor_get_geometry(monitor, &geometry)
scale := int(C.gdk_monitor_get_scale_factor(monitor))
return int(x), int(y), &Screen{
ID: fmt.Sprintf("%d", 0), // A unique identifier for the display
Name: C.GoString(C.gdk_monitor_get_model(monitor)), // The name of the display
Scale: float32(scale), // The scale factor of the display
X: int(geometry.x), // The x-coordinate of the top-left corner of the rectangle
Y: int(geometry.y), // The y-coordinate of the top-left corner of the rectangle
Size: Size{Width: int(geometry.width), Height: int(geometry.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
}
}
func (w *linuxWebviewWindow) destroy() {
w.parent.markAsDestroyed()
// Free menu
@@ -708,7 +734,8 @@ func (w *linuxWebviewWindow) destroy() {
C.gtk_widget_destroy((*C.GtkWidget)(w.gtkmenu))
w.gtkmenu = nil
}
w.destroy()
// Free window
C.gtk_widget_destroy(w.gtkWidget())
}
func (w *linuxWebviewWindow) fullscreen() {
@@ -908,7 +935,7 @@ func (w *linuxWebviewWindow) show() {
return
}
C.gtk_widget_show_all(w.gtkWidget())
w.setAbsolutePosition(w.lastX, w.lastY)
//w.setAbsolutePosition(w.lastX, w.lastY)
}
func windowIgnoreMouseEvents(window pointer, webview pointer, ignore bool) {
@@ -1057,6 +1084,19 @@ func handleDeleteEvent(widget *C.GtkWidget, event *C.GdkEvent, data C.uintptr_t)
return C.gboolean(1)
}
//export handleFocusEvent
func handleFocusEvent(widget *C.GtkWidget, event *C.GdkEvent, data C.uintptr_t) C.gboolean {
focusEvent := (*C.GdkEventFocus)(unsafe.Pointer(event))
if focusEvent._type == C.GDK_FOCUS_CHANGE {
if focusEvent.in == C.TRUE {
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowFocusIn))
} else {
processWindowEvent(C.uint(data), C.uint(events.Linux.WindowFocusOut))
}
}
return C.gboolean(0)
}
//export handleLoadChanged
func handleLoadChanged(webview *C.WebKitWebView, event C.WebKitLoadEvent, data C.uintptr_t) {
switch event {
@@ -1075,6 +1115,7 @@ func (w *linuxWebviewWindow) setupSignalHandlers(emit func(e events.WindowEventT
// Set up the window close event
wv := unsafe.Pointer(w.webview)
C.signal_connect(unsafe.Pointer(w.window), c.String("delete-event"), C.handleDeleteEvent, winID)
C.signal_connect(unsafe.Pointer(w.window), c.String("focus-out-event"), C.handleFocusEvent, winID)
C.signal_connect(wv, c.String("load-changed"), C.handleLoadChanged, winID)
contentManager := C.webkit_web_view_get_user_content_manager(w.webKitWebView())
@@ -1111,6 +1152,14 @@ func (w *linuxWebviewWindow) setupSignalHandlers(emit func(e events.WindowEventT
C.signal_connect(wv, c.String("key-press-event"), C.onKeyPressEvent, winID)
}
func getMouseButtons() (bool, bool, bool) {
var pointer *C.GdkDevice
var state C.GdkModifierType
pointer = C.gdk_seat_get_pointer(C.gdk_display_get_default_seat(C.gdk_display_get_default()))
C.gdk_device_get_state(pointer, nil, nil, &state)
return state&C.GDK_BUTTON1_MASK > 0, state&C.GDK_BUTTON2_MASK > 0, state&C.GDK_BUTTON3_MASK > 0
}
func openDevTools(webview pointer) {
inspector := C.webkit_web_view_get_inspector((*C.WebKitWebView)(webview))
C.webkit_web_inspector_show(inspector)
@@ -1180,7 +1229,8 @@ func (w *linuxWebviewWindow) setZoom(zoom float64) {
}
func (w *linuxWebviewWindow) move(x, y int) {
C.gtk_window_move((*C.GtkWindow)(w.window), C.int(x), C.int(y))
// Move the window to these coordinates
C.gtk_window_move(w.gtkWindow(), C.int(x), C.int(y))
}
func (w *linuxWebviewWindow) absolutePosition() (int, int) {
@@ -1380,7 +1430,7 @@ func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden b
gtkFilters := []*C.GtkFileFilter{}
for _, filter := range filters {
f := (C.gtk_file_filter_new())
f := C.gtk_file_filter_new()
displayStr := C.CString(filter.DisplayName)
C.gtk_file_filter_set_name(f, displayStr)
C.free(unsafe.Pointer(displayStr))
+4
View File
@@ -106,6 +106,10 @@ func (m *Menu) clone() *Menu {
return result
}
func (m *Menu) Append(in *Menu) {
m.items = append(m.items, in.items...)
}
func (a *App) NewMenu() *Menu {
return &Menu{}
}
+71 -5
View File
@@ -24,6 +24,8 @@ const (
)
type linuxSystemTray struct {
parent *SystemTray
id uint
label string
icon []byte
@@ -41,6 +43,11 @@ type linuxSystemTray struct {
itemMap map[int32]*systrayMenuItem
}
func (s *linuxSystemTray) getScreen() (*Screen, error) {
_, _, result := getMousePosition()
return result, nil
}
// dbusMenu is a named struct to map into generated bindings.
// It represents the layout of a menu item
type dbusMenu = struct {
@@ -182,6 +189,22 @@ func (s *linuxSystemTray) refresh() {
}
func (s *linuxSystemTray) setMenu(menu *Menu) {
if s.parent.attachedWindow.Window != nil {
temp := menu
menu = NewMenu()
title := "Open"
if s.parent.attachedWindow.Window.Name() != "" {
title += " " + s.parent.attachedWindow.Window.Name()
} else {
title += " window"
}
openMenuItem := menu.Add(title)
openMenuItem.OnClick(func(*Context) {
s.parent.clickHandler()
})
menu.AddSeparator()
menu.Append(temp)
}
s.itemMap = map[int32]*systrayMenuItem{}
// our root menu element
s.itemMap[0] = &systrayMenuItem{
@@ -198,16 +221,47 @@ func (s *linuxSystemTray) setMenu(menu *Menu) {
}
func (s *linuxSystemTray) positionWindow(window *WebviewWindow, offset int) error {
// Get the mouse location on the screen
mouseX, mouseY, currentScreen := getMousePosition()
screenBounds := currentScreen.Size
// Calculate new X position
newX := mouseX - (window.Width() / 2)
// Check if the window goes out of the screen bounds on the left side
if newX < 0 {
newX = 0
}
// Check if the window goes out of the screen bounds on the right side
if newX+window.Width() > screenBounds.Width {
newX = screenBounds.Width - window.Width()
}
// Calculate new Y position
newY := mouseY - (window.Height() / 2)
// Check if the window goes out of the screen bounds on the top
if newY < 0 {
newY = 0
}
// Check if the window goes out of the screen bounds on the bottom
if newY+window.Height() > screenBounds.Height {
newY = screenBounds.Height - window.Height() - offset
}
// Set the new position of the window
window.SetAbsolutePosition(newX, newY)
return nil
}
func (s *linuxSystemTray) getScreen() (*Screen, error) {
// FIXME: How do we get the screen we are on?
return &Screen{}, nil
}
func (s *linuxSystemTray) bounds() (*Rect, error) {
// Best effort guess at the screen bounds
return &Rect{}, nil
}
func (s *linuxSystemTray) run() {
@@ -354,6 +408,7 @@ func newSystemTrayImpl(s *SystemTray) systemTrayImpl {
}
return &linuxSystemTray{
parent: s,
id: s.id,
label: label,
icon: s.icon,
@@ -572,6 +627,16 @@ func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, tim
InvokeAsync(item.menuItem.handleClick)
}
}
if eventID == "opened" {
if s.parent.onMenuOpen != nil {
s.parent.onMenuOpen()
}
}
if eventID == "closed" {
if s.parent.onMenuClose != nil {
s.parent.onMenuClose()
}
}
return
}
@@ -583,6 +648,7 @@ func (s *linuxSystemTray) EventGroup(events []struct {
V3 uint32
}) (idErrors []int32, err *dbus.Error) {
for _, event := range events {
fmt.Printf("EventGroup: %v, %v, %v, %v\n", event.V0, event.V1, event.V2, event.V3)
if event.V1 == "clicked" {
item, ok := s.itemMap[event.V0]
if ok {
@@ -264,6 +264,12 @@ func (w *linuxWebviewWindow) run() {
w.execJS(js)
}
})
w.parent.On(events.Linux.WindowFocusIn, func(e *WindowEvent) {
w.parent.emit(events.Common.WindowFocus)
})
w.parent.On(events.Linux.WindowFocusOut, func(e *WindowEvent) {
w.parent.emit(events.Common.WindowLostFocus)
})
w.parent.On(events.Linux.WindowDeleteEvent, func(e *WindowEvent) {
w.parent.emit(events.Common.WindowClosing)
})