Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/PanelWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,57 @@ public class Wingpanel.PanelWindow : Gtk.Window {
return;
} else {
Services.BackgroundManager.get_default ().remember_window ();

// Panels are never granted keyboard focus automatically by the
// compositor; it must be requested explicitly. Without it a
// popover opened from a keybinding has no keyboard focus and
// typing (e.g. in the Applications Menu search field) does not
// reach it.
if (desktop_panel != null) {
desktop_panel.focus ();
// focus () is an asynchronous request. The popover is
// popped up immediately after this returns, so without
// waiting for the compositor to actually grant focus the
// popover comes up unfocused and never gets mapped.
unowned var display = Gdk.Display.get_default ();
if (display is Gdk.Wayland.Display) {
((Gdk.Wayland.Display) display).get_wl_display ().roundtrip ();
}
}
}
});

Services.BackgroundManager.get_default ().background_state_changed.connect (update_background);
}

private bool realize_handled = false;
private void on_realize () {
// A widget can be realized more than once. Connecting the surface
// signals again would run them multiple times per frame, and init_wl ()
// would re-request our panel object and get us killed by the
// compositor, so only ever do this once.
if (realize_handled) {
return;
}

realize_handled = true;

unowned var surface = (Gdk.Toplevel) get_surface ();
surface.compute_size.connect (on_compute_size);
surface.layout.connect (on_layout);

surface.enter_monitor.connect (on_enter_monitor);

// The popover takes no Wayland grab (see PopoverManager) so it will not
// dismiss itself. Watch the toplevel's focus state instead and close it
// when the panel loses keyboard focus.
surface.notify["state"].connect (() => {
var focused = (surface.state & Gdk.ToplevelState.FOCUSED) != 0;
if (!focused && popover_manager.indicator_open) {
popover_manager.close ();
}
});

init_wl ();
}

Expand Down Expand Up @@ -103,6 +141,15 @@ public class Wingpanel.PanelWindow : Gtk.Window {

public void registry_handle_global (Wl.Registry wl_registry, uint32 name, string @interface, uint32 version) {
if (@interface == "io_elementary_pantheon_shell_v1") {
// get_panel () may only be requested once per wl_surface: asking
// twice makes the compositor kill us with a protocol error
// ("get_panel already requested"), which takes the whole panel
// down. realize can fire more than once, so bail out if we already
// have our panel object.
if (desktop_panel != null) {
return;
}

desktop_shell = wl_registry.bind<Pantheon.Desktop.Shell> (name, ref Pantheon.Desktop.Shell.iface, uint32.min (version, 1));
unowned var window = get_surface ();
if (window is Gdk.Wayland.Surface) {
Expand Down
39 changes: 34 additions & 5 deletions src/Services/PopoverManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,24 @@ public class Wingpanel.Services.PopoverManager : Object {
_current_indicator.display_widget.has_tooltip = true;
_current_indicator.base_indicator.closed ();
_current_indicator = value;
popover.unparent ();
}

if (_current_indicator != null) {
popover.child = _current_indicator.indicator_widget;
popover.set_parent (_current_indicator);
// Only reparent when the popover actually needs to move to a
// different indicator. Unparenting destroys the popover's
// surface and set_parent () builds a new one; doing that on
// every open/close cycle eventually leaves the popover
// unmapped (visible but never drawn) and ends in a Wayland
// protocol error.
if (popover.parent != _current_indicator) {
if (popover.parent != null) {
popover.unparent ();
}

popover.set_parent (_current_indicator);
}

popover.popup ();

_current_indicator.set_state_flags (CHECKED, true);
Expand All @@ -63,16 +75,33 @@ public class Wingpanel.Services.PopoverManager : Object {
}
}

public void close () {
current_indicator = null;
}

construct {
popover = new Gtk.Popover () {
has_arrow = false,
position = BOTTOM
position = BOTTOM,
// autohide makes GTK request an xdg_popup grab. Wayland only grants
// that grab against a recent input event serial, and a popover
// opened from a keybinding has received no input at all, so the
// compositor refuses the popup and it is never mapped. Dismissal
// is handled by PanelWindow watching the toplevel focus state.
autohide = false
};
popover.add_css_class ("indicator");

popover.closed.connect (() => {
current_indicator = null;
popover.unparent ();
// Fires for closes the setter already handled (it nulls
// _current_indicator before the asynchronous popdown), so guard
// against dereferencing null. Deliberately does NOT unparent: the
// popover stays parented to its indicator and is only moved when a
// different indicator is opened.
if (_current_indicator != null) {
_current_indicator.set_state_flags (NORMAL, true);
current_indicator = null;
}
});
}
}
Loading