Wayland: simplify handling of tall menu windows
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled

This commit is contained in:
ManoloFLTK
2025-03-14 12:29:17 +01:00
parent c18c39071a
commit 24aec69f27
@@ -1347,13 +1347,20 @@ bool Fl_Wayland_Window_Driver::process_menu_or_tooltip(struct wld_window *new_wi
xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1);
popup_y++;
}
int V, screen_H;
Fl::screen_work_area(V, V, V, screen_H, origin_win->screen_num());
// Under KWIN, we need to have in screen_H the size of the current screen's work-area.
// We get this exactly only when there's a single screen. Otherwise, we get the screen height.
bool tall_kde = (pWindow->h() > screen_H) &&
(Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN);
xdg_positioner_set_size(positioner, pWindow->w() * f , (tall_kde ? screen_H : pWindow->h()) * f );
int positioner_H = pWindow->h();
if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN) {
// Under KWIN, limiting the height of the positioner to the work area height
// results in tall popup windows starting at the top of the screen, what we want.
// Unfortunately, we know the work area height exactly only for single-screen systems,
// otherwise FLTK returns work area height == screen height. In that case we estimate
// work area height ≈ screen height - 40.
int V, work_area_H, screen_H;
Fl::screen_work_area(V, V, V, work_area_H, origin_win->screen_num());
Fl::screen_xywh(V, V, V, screen_H, origin_win->screen_num());
if (work_area_H == screen_H) work_area_H -= 40;
if (positioner_H > work_area_H) positioner_H = work_area_H;
}
xdg_positioner_set_size(positioner, pWindow->w() * f , positioner_H * f );
xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);
xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
// prevent menuwindow from expanding beyond display limits
@@ -2012,83 +2019,19 @@ void Fl_Wayland_Window_Driver::subRect(cairo_rectangle_int_t *r) {
void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) {
if (y == pWindow->y()) return;
if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN) {
// The KWin compositor refuses to position a popup such that it extends above
// the top of the screen. That's no longer true with version 6 of KWIN.
// Instead of sliding the popup window
// on the display, we slide the drawing inside the fixed popup via
// member variable offset_y of the menuwindow class, and we redraw the popup
// content. It's also useful to make such tall popup window transparent.
*Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y());
struct wld_window *xid = fl_wl_xid(pWindow);
wl_surface_set_opaque_region(xid->wl_surface, NULL);
if (xid->buffer) memset(xid->buffer->draw_buffer.buffer, 0,
xid->buffer->draw_buffer.data_size);
//printf("offset_y=%d\n", *Fl_Window_Driver::menu_offset_y(pWindow));
this->y(y);
pWindow->redraw();
return;
}
struct wld_window * xid_menu = fl_wl_xid(pWindow);
//printf("reposition %dx%d[cur=%d] menu->state=%d\n", x, y, pWindow->y(), xid_menu->state);
struct xdg_popup *old_popup = xid_menu->xdg_popup;
struct xdg_surface *old_xdg = xid_menu->xdg_surface;
struct wl_surface *old_surface = xid_menu->wl_surface;
// menu_origin will be the parent of the processed menu window
Fl_Window *menu_origin = Fl_Window_Driver::menu_title(pWindow);
if (!menu_origin) menu_origin = Fl_Window_Driver::menu_leftorigin(pWindow);
if (!menu_origin) menu_origin = Fl_Window_Driver::menu_parent();
if (Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_bartitle(pWindow) &&
!Fl_Window_Driver::menu_leftorigin(pWindow)) {
// occurs with tall popup menu
menu_origin = Fl_Window_Driver::menu_parent();
}
// create a new popup at position (x,y) and display it above the current one
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
xid_menu->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
wl_surface_add_listener(xid_menu->wl_surface, &surface_listener, xid_menu);
xid_menu->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base,
xid_menu->wl_surface);
xdg_surface_add_listener(xid_menu->xdg_surface, &xdg_surface_listener, xid_menu);
struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
struct wld_window * parent_xid = fl_wl_xid(menu_origin);
float f = Fl::screen_scale(Fl_Window_Driver::menu_parent()->screen_num());
int popup_x = x * f, popup_y = y * f + xid_menu->state;
if (menu_origin->menu_window() && driver(menu_origin)->popup_window()) {
popup_x -= menu_origin->x() * f;
popup_y -= menu_origin->y() * f;
}
if (popup_x >= menu_origin->w() * f) popup_x = menu_origin->w() * f - 1;
if (parent_xid->kind == DECORATED) {
libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y,
&popup_x, &popup_y);
}
xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1);
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);
xdg_positioner_set_constraint_adjustment(positioner,
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X);
xdg_positioner_set_offset(positioner, 0, popup_y);
xid_menu->xdg_popup = xdg_surface_get_popup(xid_menu->xdg_surface, parent_xid->xdg_surface,
positioner);
xdg_positioner_destroy(positioner);
struct win_positioner *win_pos = new struct win_positioner;
win_pos->window = xid_menu;
win_pos->x = popup_x;
win_pos->y = popup_y;
win_pos->child_popup = NULL;
xdg_popup_add_listener(xid_menu->xdg_popup, &popup_listener, win_pos);
wl_surface_commit(xid_menu->wl_surface);
wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); // necessary with sway
// delete the previous popup
struct win_positioner *old_win_pos =
(struct win_positioner*)xdg_popup_get_user_data(old_popup);
xdg_popup_destroy(old_popup);
delete old_win_pos;
xdg_surface_destroy(old_xdg);
destroy_surface_caution_pointer_focus(old_surface, scr_driver->seat);
// The top of the tall popup window was positioned at the top of the screen
// Instead of sliding up the popup window on the display, we slide up the
// drawing inside the fixed popup via member variable offset_y of the
// menuwindow class, and we redraw the popup content.
// It's also useful to make such tall popup window transparent.
*Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y());
struct wld_window *xid = fl_wl_xid(pWindow);
wl_surface_set_opaque_region(xid->wl_surface, NULL);
if (xid->buffer) memset(xid->buffer->draw_buffer.buffer, 0,
xid->buffer->draw_buffer.data_size);
//printf("offset_y=%d\n", *Fl_Window_Driver::menu_offset_y(pWindow));
this->y(y);
pWindow->redraw();
}