|
12 | 12 | #include "../../image.h" |
13 | 13 | #include "../../menu.h" |
14 | 14 | #include "../../menu_event.h" |
| 15 | +#include "../../window.h" |
15 | 16 |
|
16 | 17 | namespace nativeapi { |
17 | 18 |
|
@@ -657,29 +658,103 @@ bool Menu::Open(const PositioningStrategy& strategy, Placement placement) { |
657 | 658 | if (pimpl_->gtk_menu_) { |
658 | 659 | gtk_widget_show_all(pimpl_->gtk_menu_); |
659 | 660 |
|
660 | | - double x = 0, y = 0; |
| 661 | + // Try to get GdkWindow from strategy if it's Relative type with a window |
| 662 | + GdkWindow* gdk_window = nullptr; |
| 663 | + GdkRectangle rectangle; |
661 | 664 | bool use_explicit_position = false; |
662 | 665 |
|
663 | 666 | // Determine position based on strategy type |
664 | 667 | switch (strategy.GetType()) { |
665 | | - case PositioningStrategy::Type::Absolute: |
666 | | - x = strategy.GetAbsolutePosition().x; |
667 | | - y = strategy.GetAbsolutePosition().y; |
| 668 | + case PositioningStrategy::Type::Absolute: { |
| 669 | + Point abs_pos = strategy.GetAbsolutePosition(); |
| 670 | + rectangle.x = static_cast<int>(abs_pos.x); |
| 671 | + rectangle.y = static_cast<int>(abs_pos.y); |
| 672 | + rectangle.width = 1; |
| 673 | + rectangle.height = 1; |
668 | 674 | use_explicit_position = true; |
| 675 | + // Use root window for absolute positioning |
| 676 | + gdk_window = gdk_get_default_root_window(); |
669 | 677 | break; |
| 678 | + } |
670 | 679 |
|
671 | | - case PositioningStrategy::Type::CursorPosition: |
672 | | - // Will use gtk_menu_popup_at_pointer |
| 680 | + case PositioningStrategy::Type::CursorPosition: { |
| 681 | + // Will use gtk_menu_popup_at_pointer or mouse position |
673 | 682 | use_explicit_position = false; |
674 | 683 | break; |
| 684 | + } |
675 | 685 |
|
676 | 686 | case PositioningStrategy::Type::Relative: { |
677 | | - Rectangle rect = strategy.GetRelativeRectangle(); |
678 | | - Point offset = strategy.GetRelativeOffset(); |
679 | | - // Position at top-left corner of rectangle plus offset |
680 | | - x = rect.x + offset.x; |
681 | | - y = rect.y + offset.y; |
682 | | - use_explicit_position = true; |
| 687 | + const Window* relative_window = strategy.GetRelativeWindow(); |
| 688 | + if (relative_window) { |
| 689 | + // Strategy was created with a Window - use its GdkWindow |
| 690 | + void* native_obj = relative_window->GetNativeObject(); |
| 691 | + if (native_obj) { |
| 692 | + gdk_window = static_cast<GdkWindow*>(native_obj); |
| 693 | + |
| 694 | + // Get window frame extents for accurate positioning |
| 695 | + GdkRectangle frame_rectangle; |
| 696 | + gdk_window_get_frame_extents(gdk_window, &frame_rectangle); |
| 697 | + |
| 698 | + // Get window position |
| 699 | + Point window_pos = relative_window->GetPosition(); |
| 700 | + |
| 701 | + // Try to get GtkWindow for title bar height calculation |
| 702 | + int title_bar_height = 0; |
| 703 | + GtkWindow* gtk_window = nullptr; |
| 704 | + // Find GtkWindow from GdkWindow by iterating through all toplevel windows |
| 705 | + GList* toplevels = gtk_window_list_toplevels(); |
| 706 | + for (GList* l = toplevels; l != nullptr; l = l->next) { |
| 707 | + GtkWindow* candidate = GTK_WINDOW(l->data); |
| 708 | + GdkWindow* candidate_gdk = gtk_widget_get_window(GTK_WIDGET(candidate)); |
| 709 | + if (candidate_gdk == gdk_window) { |
| 710 | + gtk_window = candidate; |
| 711 | + break; |
| 712 | + } |
| 713 | + } |
| 714 | + g_list_free(toplevels); |
| 715 | + |
| 716 | + if (gtk_window) { |
| 717 | + GtkWidget* titlebar = gtk_window_get_titlebar(gtk_window); |
| 718 | + if (titlebar) { |
| 719 | + title_bar_height = gtk_widget_get_allocated_height(titlebar); |
| 720 | + } |
| 721 | + } |
| 722 | + |
| 723 | + // Get relative rectangle and offset |
| 724 | + Rectangle rect = strategy.GetRelativeRectangle(); |
| 725 | + Point offset = strategy.GetRelativeOffset(); |
| 726 | + |
| 727 | + // Calculate position: relative position (already in screen coordinates from GetRelativeRectangle) |
| 728 | + // + offset, then adjust for frame extents and title bar |
| 729 | + // Note: GetRelativeRectangle() returns window bounds in screen coordinates, |
| 730 | + // so we need to add the offset and adjust for frame/titlebar |
| 731 | + rectangle.x = static_cast<int>(rect.x + offset.x - frame_rectangle.x); |
| 732 | + rectangle.y = static_cast<int>(rect.y + offset.y - frame_rectangle.y + title_bar_height); |
| 733 | + rectangle.width = 1; |
| 734 | + rectangle.height = 1; |
| 735 | + use_explicit_position = true; |
| 736 | + } else { |
| 737 | + // Fallback: use root window |
| 738 | + gdk_window = gdk_get_default_root_window(); |
| 739 | + Rectangle rect = strategy.GetRelativeRectangle(); |
| 740 | + Point offset = strategy.GetRelativeOffset(); |
| 741 | + rectangle.x = static_cast<int>(rect.x + offset.x); |
| 742 | + rectangle.y = static_cast<int>(rect.y + offset.y); |
| 743 | + rectangle.width = 1; |
| 744 | + rectangle.height = 1; |
| 745 | + use_explicit_position = true; |
| 746 | + } |
| 747 | + } else { |
| 748 | + // Strategy was created with a Rectangle - use root window |
| 749 | + Rectangle rect = strategy.GetRelativeRectangle(); |
| 750 | + Point offset = strategy.GetRelativeOffset(); |
| 751 | + rectangle.x = static_cast<int>(rect.x + offset.x); |
| 752 | + rectangle.y = static_cast<int>(rect.y + offset.y); |
| 753 | + rectangle.width = 1; |
| 754 | + rectangle.height = 1; |
| 755 | + use_explicit_position = true; |
| 756 | + gdk_window = gdk_get_default_root_window(); |
| 757 | + } |
683 | 758 | break; |
684 | 759 | } |
685 | 760 | } |
@@ -739,24 +814,47 @@ bool Menu::Open(const PositioningStrategy& strategy, Placement placement) { |
739 | 814 | break; |
740 | 815 | } |
741 | 816 |
|
742 | | - // Try to position at explicit coordinates if available |
| 817 | + // Position menu |
743 | 818 | if (use_explicit_position) { |
744 | | - GdkWindow* root_window = gdk_get_default_root_window(); |
745 | | - if (!root_window) { |
| 819 | + if (!gdk_window) { |
| 820 | + // Fallback to root window if no window available |
| 821 | + gdk_window = gdk_get_default_root_window(); |
| 822 | + } |
| 823 | + if (!gdk_window) { |
746 | 824 | // No root window (e.g., Wayland) and no parent to anchor to → cannot show |
747 | 825 | return false; |
748 | 826 | } |
749 | | - |
750 | | - GdkRectangle rect; |
751 | | - rect.x = static_cast<int>(x); |
752 | | - rect.y = static_cast<int>(y); |
753 | | - rect.width = 1; |
754 | | - rect.height = 1; |
755 | | - gtk_menu_popup_at_rect(GTK_MENU(pimpl_->gtk_menu_), root_window, &rect, anchor_gravity, |
756 | | - menu_gravity, nullptr); |
| 827 | + gtk_menu_popup_at_rect(GTK_MENU(pimpl_->gtk_menu_), gdk_window, &rectangle, |
| 828 | + anchor_gravity, menu_gravity, nullptr); |
757 | 829 | return true; |
758 | 830 | } else { |
759 | | - // Let GTK automatically use the current event |
| 831 | + // CursorPosition: Try to get mouse position relative to a window if available |
| 832 | + const Window* relative_window = strategy.GetRelativeWindow(); |
| 833 | + if (relative_window) { |
| 834 | + void* native_obj = relative_window->GetNativeObject(); |
| 835 | + if (native_obj) { |
| 836 | + GdkWindow* target_window = static_cast<GdkWindow*>(native_obj); |
| 837 | + GdkDevice* mouse_device; |
| 838 | +#if GTK_CHECK_VERSION(3, 20, 0) |
| 839 | + GdkSeat* seat = gdk_display_get_default_seat(gdk_display_get_default()); |
| 840 | + mouse_device = gdk_seat_get_pointer(seat); |
| 841 | +#else |
| 842 | + GdkDeviceManager* devman = |
| 843 | + gdk_display_get_device_manager(gdk_display_get_default()); |
| 844 | + mouse_device = gdk_device_manager_get_client_pointer(devman); |
| 845 | +#endif |
| 846 | + int x, y; |
| 847 | + gdk_window_get_device_position(target_window, mouse_device, &x, &y, nullptr); |
| 848 | + rectangle.x = x; |
| 849 | + rectangle.y = y; |
| 850 | + rectangle.width = 1; |
| 851 | + rectangle.height = 1; |
| 852 | + gtk_menu_popup_at_rect(GTK_MENU(pimpl_->gtk_menu_), target_window, &rectangle, |
| 853 | + anchor_gravity, menu_gravity, nullptr); |
| 854 | + return true; |
| 855 | + } |
| 856 | + } |
| 857 | + // Fallback: Let GTK automatically use the current event |
760 | 858 | gtk_menu_popup_at_pointer(GTK_MENU(pimpl_->gtk_menu_), nullptr); |
761 | 859 | return true; |
762 | 860 | } |
|
0 commit comments