mirror of
https://github.com/fltk/fltk.git
synced 2026-05-23 15:56:10 +08:00
Wayland: simplify handling of tall menu windows
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user