Skip to content

Commit

Permalink
Fixed the menu location in order to fit on the display it is activated (
Browse files Browse the repository at this point in the history
#583)

* Fixed the menu location in order to fit on the display it is activated

This issue was added when I remove a old workaround for multiple display
setup and didn't pay attention that it was also making the menu window
to fit on a display. I revert the commit and rewrote it.

It's not perfect for setups where the displays have different resolution.
In that situation if a window is shared by multiple displays in a location
where a window is scaled the correct dimensions can not be safely evaluated
and then we can still get the popup menu cropped.


* Fixed the menu selection that was letting garbage
This issue was introduced by the fix for another menu issue that was crashing
Wings3D when closing a window with the context menu active (commit: #be68730)

The fix redraw the entire menu unselected before it be displayed. It was also
needed to find for the window of the submenu item because it's not stored in
the object property of menudata.

NOTE: Fixed popup menu location that could be shown out of screen. Thanks to Xavier

 Fixed the menu selection that was showing garbage. Thanks to sciroccorics
  • Loading branch information
Micheus authored Oct 1, 2024
1 parent 265681a commit 5afc4fe
Showing 1 changed file with 51 additions and 3 deletions.
54 changes: 51 additions & 3 deletions src/wings_menu.erl
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ setup_dialog(Parent, Entries, Magnet, ScreenPos, Cache) ->
end,
MenuData;
#{frame := Frame} = MenuData ->
wxWindow:move(Frame, ScreenPos),
Entries0 = maps:get(entries, MenuData),
Col = maps:get(colors, MenuData),
menu_sel_cleanup(Col, Entries0),

Pos = fit_menu_on_display(Frame,ScreenPos),
wxWindow:move(Frame, Pos),
wxPopupTransientWindow:popup(Frame),
MenuData
end.
Expand All @@ -245,7 +250,7 @@ do_setup_dialog(TopParent, Entries0, Magnet, ScreenPos) ->
wxPanel:setSizer(Panel, Main),
wxSizer:fit(Main, Panel),
wxWindow:setClientSize(Frame, wxWindow:getSize(Panel)),
wxWindow:move(Frame, ScreenPos),
wxWindow:move(Frame, fit_menu_on_display(Frame, ScreenPos)),
show_menu_frame(Overlay, Frame, KbdFocus),
#{overlay=>Overlay, frame=>Frame, panel=>Panel, entries=>Entries, colors=>Cols}.

Expand Down Expand Up @@ -391,8 +396,9 @@ popup_events(MenuData, Magnet, Previous, Ns, Owner) ->
end;
cancel ->
wings_wm:psend(Owner, cancel);
{move, Pos} ->
{move, {X,Y}} ->
Frame = maps:get(frame, MenuData),
Pos = fit_menu_on_display(Frame, {X,Y}),
wxWindow:move(Frame, Pos),
wings_wm:psend(Owner, redraw),
popup_events(MenuData, Magnet, Previous, Ns, Owner);
Expand All @@ -403,6 +409,30 @@ popup_events(MenuData, Magnet, Previous, Ns, Owner) ->
popup_events(MenuData, Magnet, Previous, Ns, Owner)
end.

fit_menu_on_display(Frame, {MX,MY} = Pos) ->
{WW,WH} = wxWindow:getSize(Frame),
%% When multiple resolution displays are present, there is a situation which
%% the window being shared partially by two of them - and the window being
%% scaled up - the Display ID returned is -1. In order to avoid a crash it we
%% get the ID for the main window (Frame's parent) - the menu is shown on it.
DisplayID =
case wxDisplay:getFromPoint(Pos) of
-1 -> wxDisplay:getFromWindow(wxWindow:getParent(Frame));
Id -> Id
end,
Display = wxDisplay_new(DisplayID),
{DX,DY,DW,DH} = wxDisplay:getClientArea(Display),
MaxW = abs(DX-MX)+WW,
PX = if MaxW > DW -> (DX+DW)-(WW+5);
true -> max(DX+5, (MX-5)) %% Move so mouse is inside menu
end,
MaxH = abs(DY-MY)+WH,
PY = if MaxH > DH -> (DY+DH)-(WH+5);
true -> max(DY+5, (MY-5)) %% Move so mouse is inside menu
end,
wxDisplay:destroy(Display),
{PX,PY}.

%% If the mouse is not moved after popping up the menu, the menu entry
%% is not active, find_active_panel finds the active row.
find_active_panel(Panel, MX, MY) ->
Expand Down Expand Up @@ -1104,6 +1134,24 @@ menu_item_desc(Desc, HotKey) ->
_ -> Desc ++ "\t" ++ HotKey
end.

menu_sel_cleanup(_, []) -> ok;
menu_sel_cleanup({BG,FG}=Col, [#menu{type=submenu, object=undefined, wxid=Id}|Menu]) ->
Panel = wxWindow:findWindowById(Id),
Set = fun() ->
setup_colors([Panel|wxWindow:getChildren(Panel)], BG, FG)
end,
wx:batch(Set),
menu_sel_cleanup(Col,Menu);
menu_sel_cleanup({BG,FG}=Col, [#menu{type=menu, object=Obj}|Menu]) ->
Panel = maps:get(panel, Obj),
Set = fun() ->
setup_colors(Panel, BG, FG)
end,
wx:batch(Set),
menu_sel_cleanup(Col,Menu);
menu_sel_cleanup(Col, [_|Menu]) ->
menu_sel_cleanup(Col,Menu).

%% We want to use the predefined id where they exist (mac) needs for it's
%% specialized menus but we want our shortcuts hmm.
%% We also get little predefined icons for OS's that have that.
Expand Down

0 comments on commit 5afc4fe

Please sign in to comment.