From e73b2da5e441f8d9f56ee7c1a9e9cbda269653bf Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Tue, 27 Dec 2022 13:15:31 +0100 Subject: Wayland: Dropdown menu moves when navigated (#613) - cont'd Menu windows containing sub-menus are now processed differently. --- src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx | 53 ++++++++++++++++++++---- 1 file changed, 46 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx index f5399af0c..fd912b9a0 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include // for ceil() #include // for pid_t @@ -950,6 +951,44 @@ static const char *get_prog_name() { } +/* Implementation note about menu windows under Wayland. + Wayland offers a way to position popup windows such as menu windows using constraints + but hides the position of the window inside the display. Each popup is located relatively + to a parent window which can be a popup itself and MUST overlap or at least touch this parent. + FLTK computes the adequate position of a menu window in the display and then maps it + at that position. + These 2 logics are quite different. + The approach implemented here is two-fold. + 1) If a menu window is not taller than the display and contains no submenu, use Wayland + logic to position it. The benefit is that window menus become authorized to lay outside + the parent window but Wayland will not make them run beyond display limits. + We avoid submenu-containing popups because these could lead to + locate the future submenu outside its parent window, which Wayland forbids. + We avoid very tall menu windows because navigating with FLTK inside them would require to know + what part of them is visible which Wayland hides. + 2) Otherwise, have FLTK compute the menu position under the constraint that its active item + must be inside the menu-containing window. This constraint ensures Wayland will accept this + position because the required overlap is satisfied. + Function use_wayland_menu_positioning() below determines wether 1) or 2) is used for any + window menu. The result of this function is stored in the state member of the menu window's + struct wld_window for fast re-use. + */ + +//returns true if win is a menuwindow without submenu and is not taller than display +static bool use_wayland_menu_positioning(Fl_Window *win, Fl_Window *parent_win) { + if (!win->menu_window()) return true; + int XX, YY, WW, HH; + Fl::screen_xywh(XX, YY, WW, HH, parent_win->screen_num()); + if (win->h() > HH) return false; + const Fl_Menu_Item *m = Fl_Window_Driver::driver(win)->current_menu(); + while (m->label()) { + if (m->flags & (FL_SUBMENU | FL_SUBMENU_POINTER)) return false; + m = m->next(); + } + return true; +} + + Fl_X *Fl_Wayland_Window_Driver::makeWindow() { struct wld_window *new_window; @@ -1005,10 +1044,9 @@ Fl_X *Fl_Wayland_Window_Driver::makeWindow() xdg_positioner_set_size(positioner, pWindow->w() * f , pWindow->h() * f ); xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); - int XX, YY, WW, HH; - Fl::screen_xywh(XX, YY, WW, HH, parent_win->screen_num()); - if (pWindow->h() <= HH) { - // prevent menuwindow from expanding beyond display bottom + new_window->state = use_wayland_menu_positioning(pWindow, parent_win); + if (new_window->state) { + // prevent menuwindow from expanding beyond display limits xdg_positioner_set_constraint_adjustment(positioner, XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); } @@ -1557,9 +1595,10 @@ void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) { void Fl_Wayland_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) { Fl_Window *parent = Fl_Window_Driver::menu_parent(); if (parent) { - int XX,YY,WW,HH; - Fl::screen_xywh(XX, YY, WW, HH, parent->screen_num()); - if (pWindow->h() > HH) { // the menu window is higher than the display + struct wld_window *xid = fl_wl_xid(pWindow); + bool condition = xid ? xid->state : use_wayland_menu_positioning(pWindow, parent); + if (!condition) { + // keep active menu part inside parent window X = parent->x(); Y = parent->y(); W = parent->w(); -- cgit v1.2.3