@@ -655,211 +655,143 @@ bool Menu::Open(const PositioningStrategy& strategy, Placement placement) {
655655 return data.result ;
656656 }
657657
658- if (pimpl_->gtk_menu_ ) {
659- gtk_widget_show_all (pimpl_->gtk_menu_ );
660-
661- // Try to get GdkWindow from strategy if it's Relative type with a window
662- GdkWindow* gdk_window = nullptr ;
663- GdkRectangle rectangle;
664- bool use_explicit_position = false ;
665-
666- // Determine position based on strategy type
667- switch (strategy.GetType ()) {
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 ;
674- use_explicit_position = true ;
675- // Use root window for absolute positioning
676- gdk_window = gdk_get_default_root_window ();
677- break ;
678- }
658+ if (!pimpl_->gtk_menu_ ) {
659+ return false ;
660+ }
679661
680- case PositioningStrategy::Type::CursorPosition: {
681- // Will use gtk_menu_popup_at_pointer or mouse position
682- use_explicit_position = false ;
683- break ;
684- }
662+ gtk_widget_show_all (pimpl_->gtk_menu_ );
685663
686- case PositioningStrategy::Type::Relative: {
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- }
758- break ;
759- }
664+ // Get GdkWindow from relative window if available, otherwise use root window
665+ GdkWindow* gdk_window = nullptr ;
666+ const Window* relative_window = strategy.GetRelativeWindow ();
667+ if (relative_window) {
668+ void * native_obj = relative_window->GetNativeObject ();
669+ if (native_obj) {
670+ gdk_window = static_cast <GdkWindow*>(native_obj);
760671 }
672+ }
673+ if (!gdk_window) {
674+ gdk_window = gdk_get_default_root_window ();
675+ }
676+ if (!gdk_window) {
677+ // No window available (e.g., Wayland without root window) → cannot show
678+ return false ;
679+ }
761680
762- // Map placement to GDK gravity values
763- GdkGravity anchor_gravity = GDK_GRAVITY_NORTH_WEST;
764- GdkGravity menu_gravity = GDK_GRAVITY_NORTH_WEST;
765-
766- switch (placement) {
767- case Placement::Top:
768- anchor_gravity = GDK_GRAVITY_SOUTH;
769- menu_gravity = GDK_GRAVITY_NORTH;
770- break ;
771- case Placement::TopStart:
772- anchor_gravity = GDK_GRAVITY_SOUTH_WEST;
773- menu_gravity = GDK_GRAVITY_NORTH_WEST;
774- break ;
775- case Placement::TopEnd:
776- anchor_gravity = GDK_GRAVITY_SOUTH_EAST;
777- menu_gravity = GDK_GRAVITY_NORTH_EAST;
778- break ;
779- case Placement::Right:
780- anchor_gravity = GDK_GRAVITY_WEST;
781- menu_gravity = GDK_GRAVITY_EAST;
782- break ;
783- case Placement::RightStart:
784- anchor_gravity = GDK_GRAVITY_NORTH_WEST;
785- menu_gravity = GDK_GRAVITY_NORTH_EAST;
786- break ;
787- case Placement::RightEnd:
788- anchor_gravity = GDK_GRAVITY_SOUTH_WEST;
789- menu_gravity = GDK_GRAVITY_SOUTH_EAST;
790- break ;
791- case Placement::Bottom:
792- anchor_gravity = GDK_GRAVITY_NORTH;
793- menu_gravity = GDK_GRAVITY_SOUTH;
794- break ;
795- case Placement::BottomStart:
796- anchor_gravity = GDK_GRAVITY_NORTH_WEST;
797- menu_gravity = GDK_GRAVITY_SOUTH_WEST;
798- break ;
799- case Placement::BottomEnd:
800- anchor_gravity = GDK_GRAVITY_NORTH_EAST;
801- menu_gravity = GDK_GRAVITY_SOUTH_EAST;
802- break ;
803- case Placement::Left:
804- anchor_gravity = GDK_GRAVITY_EAST;
805- menu_gravity = GDK_GRAVITY_WEST;
806- break ;
807- case Placement::LeftStart:
808- anchor_gravity = GDK_GRAVITY_NORTH_EAST;
809- menu_gravity = GDK_GRAVITY_NORTH_WEST;
810- break ;
811- case Placement::LeftEnd:
812- anchor_gravity = GDK_GRAVITY_SOUTH_EAST;
813- menu_gravity = GDK_GRAVITY_SOUTH_WEST;
814- break ;
815- }
681+ GdkRectangle rectangle;
816682
817- // Position menu
818- if (use_explicit_position) {
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) {
824- // No root window (e.g., Wayland) and no parent to anchor to → cannot show
825- return false ;
826- }
827- gtk_menu_popup_at_rect (GTK_MENU (pimpl_->gtk_menu_ ), gdk_window, &rectangle,
828- anchor_gravity, menu_gravity, nullptr );
829- return true ;
830- } else {
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;
683+ // Calculate position based on strategy type
684+ if (strategy.GetType () == PositioningStrategy::Type::CursorPosition) {
685+ // Use mouse position
686+ GdkDevice* mouse_device;
838687#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);
688+ GdkSeat* seat = gdk_display_get_default_seat (gdk_display_get_default ());
689+ mouse_device = gdk_seat_get_pointer (seat);
841690#else
842- GdkDeviceManager* devman =
843- gdk_display_get_device_manager (gdk_display_get_default ());
844- mouse_device = gdk_device_manager_get_client_pointer (devman);
691+ GdkDeviceManager* devman =
692+ gdk_display_get_device_manager (gdk_display_get_default ());
693+ mouse_device = gdk_device_manager_get_client_pointer (devman);
845694#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 ;
695+ int x, y;
696+ gdk_window_get_device_position (gdk_window, mouse_device, &x, &y, nullptr );
697+ rectangle.x = x;
698+ rectangle.y = y;
699+ rectangle.width = 1 ;
700+ rectangle.height = 1 ;
701+ } else {
702+ // Absolute or Relative positioning
703+ Point position;
704+
705+ if (strategy.GetType () == PositioningStrategy::Type::Absolute) {
706+ position = strategy.GetAbsolutePosition ();
707+ // For absolute positioning, use root window coordinates directly
708+ rectangle.x = static_cast <int >(position.x );
709+ rectangle.y = static_cast <int >(position.y );
710+ } else {
711+ // Relative positioning
712+ Rectangle rect = strategy.GetRelativeRectangle ();
713+ Point offset = strategy.GetRelativeOffset ();
714+ position = Point{rect.x + offset.x , rect.y + offset.y };
715+
716+ // If we have a relative window, adjust for frame extents and title bar
717+ if (relative_window && relative_window->GetNativeObject ()) {
718+ GdkRectangle frame_rectangle;
719+ gdk_window_get_frame_extents (gdk_window, &frame_rectangle);
720+
721+ // Get title bar height from GtkWindow
722+ int title_bar_height = 0 ;
723+ GtkWindow* gtk_window = nullptr ;
724+ GList* toplevels = gtk_window_list_toplevels ();
725+ for (GList* l = toplevels; l != nullptr ; l = l->next ) {
726+ GtkWindow* candidate = GTK_WINDOW (l->data );
727+ GdkWindow* candidate_gdk = gtk_widget_get_window (GTK_WIDGET (candidate));
728+ if (candidate_gdk == gdk_window) {
729+ gtk_window = candidate;
730+ break ;
731+ }
855732 }
733+ g_list_free (toplevels);
734+
735+ if (gtk_window) {
736+ GtkWidget* titlebar = gtk_window_get_titlebar (gtk_window);
737+ if (titlebar) {
738+ title_bar_height = gtk_widget_get_allocated_height (titlebar);
739+ }
740+ }
741+
742+ rectangle.x = static_cast <int >(position.x - frame_rectangle.x );
743+ rectangle.y = static_cast <int >(position.y - frame_rectangle.y + title_bar_height);
744+ } else {
745+ // Relative to rectangle (no window) - use root window coordinates
746+ rectangle.x = static_cast <int >(position.x );
747+ rectangle.y = static_cast <int >(position.y );
856748 }
857- // Fallback: Let GTK automatically use the current event
858- gtk_menu_popup_at_pointer (GTK_MENU (pimpl_->gtk_menu_ ), nullptr );
859- return true ;
860749 }
750+
751+ // Set rectangle dimensions
752+ rectangle.width = 1 ;
753+ rectangle.height = 1 ;
861754 }
862- return false ;
755+
756+ // Map placement to GDK gravity (menu anchor)
757+ GdkGravity menu_anchor = GDK_GRAVITY_NORTH_WEST;
758+
759+ switch (placement) {
760+ case Placement::TopStart:
761+ case Placement::Top:
762+ menu_anchor = GDK_GRAVITY_SOUTH_WEST;
763+ break ;
764+ case Placement::TopEnd:
765+ menu_anchor = GDK_GRAVITY_SOUTH_EAST;
766+ break ;
767+ case Placement::BottomStart:
768+ case Placement::Bottom:
769+ menu_anchor = GDK_GRAVITY_NORTH_WEST;
770+ break ;
771+ case Placement::BottomEnd:
772+ menu_anchor = GDK_GRAVITY_NORTH_EAST;
773+ break ;
774+ case Placement::LeftStart:
775+ case Placement::Left:
776+ menu_anchor = GDK_GRAVITY_NORTH_EAST;
777+ break ;
778+ case Placement::LeftEnd:
779+ menu_anchor = GDK_GRAVITY_SOUTH_EAST;
780+ break ;
781+ case Placement::RightStart:
782+ case Placement::Right:
783+ menu_anchor = GDK_GRAVITY_NORTH_WEST;
784+ break ;
785+ case Placement::RightEnd:
786+ menu_anchor = GDK_GRAVITY_SOUTH_WEST;
787+ break ;
788+ }
789+
790+ // Position menu using gtk_menu_popup_at_rect
791+ gtk_menu_popup_at_rect (GTK_MENU (pimpl_->gtk_menu_ ), gdk_window, &rectangle,
792+ GDK_GRAVITY_NORTH_WEST, menu_anchor, nullptr );
793+
794+ return true ;
863795}
864796
865797bool Menu::Close () {
0 commit comments