Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use clutter for the background menu #1769

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b3d04cb
Prototype menu
leolost2605 Sep 27, 2023
efe7619
Add missing files
leolost2605 Sep 27, 2023
88bcd62
Add menuitems
leolost2605 Sep 27, 2023
c6f73c5
Allocate canvas size with actor size
leolost2605 Sep 27, 2023
b57ea7a
Make items selectable
leolost2605 Sep 29, 2023
b4d0521
Almost finished with menuitem api
leolost2605 Sep 29, 2023
bf1a583
Cleanup
leolost2605 Sep 29, 2023
1e52803
Use correct font
leolost2605 Sep 29, 2023
bf66104
Add separatormenuitem
leolost2605 Sep 29, 2023
dcef4f4
Make separator look right
leolost2605 Sep 30, 2023
8fa5855
Scaling works
leolost2605 Sep 30, 2023
e9c6956
Cleanup
leolost2605 Sep 30, 2023
c499ef0
Add add_separator
leolost2605 Sep 30, 2023
5742055
Finish background menu
leolost2605 Sep 30, 2023
8a8cb84
Fix scale + cleanup
leolost2605 Sep 30, 2023
ad17b0c
Use actor background color
leolost2605 Oct 1, 2023
e20c35e
Add keyboard navigation
leolost2605 Oct 1, 2023
4de20e7
Add keyboard navigation
leolost2605 Oct 1, 2023
d5fb1cd
Fix small issue
leolost2605 Oct 1, 2023
cc25b6a
Use MenuItem.with_label
leolost2605 Oct 1, 2023
a36329d
Don't use error dialogs
leolost2605 Oct 1, 2023
27191b0
Merge branch 'master' into clutter-desktop-menu
leolost2605 Oct 1, 2023
e5f8c79
Fix lint
leolost2605 Oct 1, 2023
f0b5fc9
Lookup colors
leolost2605 Oct 1, 2023
d6318c6
Update with theme
leolost2605 Oct 1, 2023
bd81770
Lookup color for text color
leolost2605 Oct 1, 2023
8250879
Fix wayland
leolost2605 Oct 1, 2023
f6340a4
Cleanup
leolost2605 Oct 1, 2023
dd188aa
More cleanup
leolost2605 Oct 1, 2023
fc7f5d6
Update meson.build order
leolost2605 Oct 1, 2023
121cae0
Fix menu position when close to monitor edges
leolost2605 Oct 1, 2023
cae29e3
Fix not correctly hiding the menu
leolost2605 Oct 1, 2023
e94192f
Merge branch 'master' into clutter-desktop-menu
lenemter Oct 26, 2023
e9c3a2e
Merge branch 'master' into clutter-desktop-menu
leolost2605 Nov 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 0 additions & 74 deletions daemon/MenuDaemon.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ namespace Gala {
private Gtk.MenuItem close;
private Gtk.MenuItem screenshot;

// Desktop Menu
private Gtk.Menu? desktop_menu = null;

private WMDBus? wm_proxy = null;

private ulong always_on_top_sid = 0U;
Expand Down Expand Up @@ -292,76 +289,5 @@ namespace Gala {
return opened ? Source.REMOVE : Source.CONTINUE;
});
}

public void show_desktop_menu (int x, int y) throws DBusError, IOError {
if (desktop_menu == null) {
var change_wallpaper = new Gtk.MenuItem.with_label (_("Change Wallpaper…"));
change_wallpaper.activate.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://desktop/appearance/wallpaper", null);
} catch (Error e) {
var message_dialog = new Granite.MessageDialog.with_image_from_icon_name (
"Failed to Open Wallpaper Settings",
"Unable to open System Settings. A handler for the `settings://` URI scheme must be installed.",
"dialog-error",
Gtk.ButtonsType.CLOSE
);
message_dialog.show_error_details (e.message);
message_dialog.run ();
message_dialog.destroy ();
}
});

var display_settings = new Gtk.MenuItem.with_label (_("Display Settings…"));
display_settings.activate.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://display", null);
} catch (Error e) {
var message_dialog = new Granite.MessageDialog.with_image_from_icon_name (
"Failed to Open Display Settings",
"Unable to open System Settings. A handler for the `settings://` URI scheme must be installed.",
"dialog-warning",
Gtk.ButtonsType.CLOSE
);
message_dialog.show_error_details (e.message);
message_dialog.run ();
message_dialog.destroy ();
}
});

var system_settings = new Gtk.MenuItem.with_label (_("System Settings…"));
system_settings.activate.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://", null);
} catch (Error e) {
var message_dialog = new Granite.MessageDialog.with_image_from_icon_name (
"Failed to Open System Settings",
"Unable to open System Settings. A handler for the `settings://` URI scheme must be installed.",
"dialog-warning",
Gtk.ButtonsType.CLOSE
);
message_dialog.show_error_details (e.message);
message_dialog.run ();
message_dialog.destroy ();
}
});

desktop_menu = new Gtk.Menu ();
desktop_menu.append (change_wallpaper);
desktop_menu.append (display_settings);
desktop_menu.append (new Gtk.SeparatorMenuItem ());
desktop_menu.append (system_settings);
desktop_menu.show_all ();
}

desktop_menu.popup (null, null, (m, ref px, ref py, out push_in) => {
var scale = m.scale_factor;
px = x / scale;
// Move the menu 1 pixel outside of the pointer or else it closes instantly
// on the mouse up event
py = (y / scale) + 1;
push_in = false;
}, Gdk.BUTTON_SECONDARY, Gdk.CURRENT_TIME);
}
}
}
22 changes: 22 additions & 0 deletions src/InternalUtils.vala
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,28 @@ namespace Gala {
);
}

private static Gtk.StyleContext color_style_context = null;
private static Gtk.CssProvider dark_style_provider = null;
public static Gdk.RGBA lookup_color (string color_name) {
if (color_style_context == null) {
color_style_context = new Gtk.StyleContext ();
}

if (Granite.Settings.get_default ().prefers_color_scheme == DARK) {
unowned var gtksettings = Gtk.Settings.get_default ();
dark_style_provider = Gtk.CssProvider.get_named (gtksettings.gtk_theme_name, "dark");
color_style_context.add_provider (dark_style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
} else if (dark_style_provider != null) {
color_style_context.remove_provider (dark_style_provider);
dark_style_provider = null;
}

Gdk.RGBA rgba;
color_style_context.lookup_color (color_name, out rgba);

return rgba;
}

/**
* Returns the workspaces geometry following the only_on_primary settings.
*/
Expand Down
44 changes: 44 additions & 0 deletions src/Widgets/BackgroundMenu.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2023 elementary, Inc. <https://elementary.io>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

public class Gala.BackgroundMenu : Menu {
public BackgroundMenu (Gala.WindowManager wm) {
Object (wm: wm);
}

construct {
var change_wallpaper = new MenuItem.with_label (_("Change Wallpaper…"));
change_wallpaper.activated.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://desktop/appearance/wallpaper", null);
} catch (Error e) {
warning ("Failed to open Wallpaper Settings: %s", e.message);
}
});

var display_settings = new MenuItem.with_label (_("Display Settings…"));
display_settings.activated.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://display", null);
} catch (Error e) {
warning ("Failed to open Display Settings: %s", e.message);
}
});

var system_settings = new MenuItem.with_label (_("System Settings…"));
system_settings.activated.connect (() => {
try {
AppInfo.launch_default_for_uri ("settings://", null);
} catch (Error e) {
warning ("Failed to open System Settings: %s", e.message);
}
});

add_menuitem (change_wallpaper);
add_menuitem (display_settings);
add_separator ();
add_menuitem (system_settings);
}
}
233 changes: 233 additions & 0 deletions src/Widgets/Menu/Menu.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* Copyright 2023 elementary, Inc. <https://elementary.io>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

public class Gala.Menu : Clutter.Actor {
public Gala.WindowManager? wm { get; construct; }

private Gala.ModalProxy modal_proxy = null;

private Clutter.Actor container;
private ShadowEffect shadow_effect;
private Clutter.Canvas canvas;

private Gtk.StyleContext style_context;
private unowned Gtk.CssProvider? dark_style_provider = null;

private MenuItem? _selected = null;
private MenuItem? selected {
get {
return _selected;
}
set {
if (_selected != null) {
_selected.selected = false;
}

_selected = value;
if (_selected != null) {
_selected.selected = true;
}
}
}

public Menu (Gala.WindowManager wm) {
Object (wm: wm);
}

construct {
var box_layout = new Clutter.BoxLayout () {
orientation = VERTICAL
};

container = new Clutter.Actor () {
layout_manager = box_layout
};

shadow_effect = new ShadowEffect (40) {
shadow_opacity = 200,
css_class = "window-switcher"
};

canvas = new Clutter.Canvas ();

layout_manager = new Clutter.BinLayout ();
add_child (container);
add_effect (shadow_effect);
set_content (canvas);

Granite.Settings.get_default ().notify["prefers-color-scheme"].connect (() => canvas.invalidate ());
Gtk.Settings.get_default ().notify["gtk-theme-name"].connect (() => canvas.invalidate ());

unowned var display = wm.get_display ();
unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (() => {
scale (display.get_monitor_scale (display.get_current_monitor ()));
});
scale (display.get_monitor_scale (display.get_current_monitor ()));

notify["allocation"].connect (() => canvas.set_size ((int) width, (int) height));

canvas.draw.connect (draw);
}

public void add_menuitem (MenuItem menuitem) {
container.add_child (menuitem);
menuitem.scale (wm.get_display ().get_monitor_scale (wm.get_display ().get_current_monitor ()));
menuitem.activated.connect (() => close_menu ());

menuitem.enter_event.connect (() => {
selected = menuitem;
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return false;
return Clutter.EVENT_PROPAGATE;

});

menuitem.leave_event.connect (() => {
selected = null;
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return false;
return Clutter.EVENT_PROPAGATE;

});
}

public void add_separator () {
var separator = new SeparatorMenuItem ();
container.add_child (separator);
separator.scale (wm.get_display ().get_monitor_scale (wm.get_display ().get_current_monitor ()));
}

public void open_menu () {
base.show ();

#if HAS_MUTTER45
//TODO: I think that's correct but didn't test it
Mtk.Rectangle rect;
wm.get_display ().get_monitor_geometry (wm.get_display ().get_current_monitor (), out rect);
#else
var rect = wm.get_display ().get_monitor_geometry (wm.get_display ().get_current_monitor ());
#endif

if (width + x > rect.x + rect.width) {
x = rect.x + rect.width - width;
}

if (height + y > rect.y + rect.height) {
y = rect.y + rect.height - height;
}

modal_proxy = wm.push_modal (this);
}

public void close_menu () {
selected = null;
wm.pop_modal (modal_proxy);

base.hide ();
}

private void scale (float scale_factor) {
canvas.scale_factor = scale_factor;
shadow_effect.scale_factor = scale_factor;

container.margin_top = container.margin_bottom = InternalUtils.scale_to_int (6, scale_factor);

foreach (var child in container.get_children ()) {
if (child is MenuItem) {
((MenuItem) child).scale (scale_factor);
continue;
}

if (child is SeparatorMenuItem) {
((SeparatorMenuItem) child).scale (scale_factor);
continue;
}
}
}

private bool draw (Cairo.Context ctx, int width, int height) {
if (style_context == null) { // gtk is not initialized yet
create_gtk_objects ();
}

ctx.save ();
ctx.set_operator (Cairo.Operator.CLEAR);
ctx.paint ();
ctx.clip ();
ctx.reset_clip ();

if (Granite.Settings.get_default ().prefers_color_scheme == DARK) {
unowned var gtksettings = Gtk.Settings.get_default ();
dark_style_provider = Gtk.CssProvider.get_named (gtksettings.gtk_theme_name, "dark");
style_context.add_provider (dark_style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
} else if (dark_style_provider != null) {
style_context.remove_provider (dark_style_provider);
dark_style_provider = null;
}

ctx.set_operator (Cairo.Operator.OVER);
style_context.render_background (ctx, 0, 0, width, height);
style_context.render_frame (ctx, 0, 0, width, height);
ctx.restore ();

return true;
}

private void create_gtk_objects () {
var window = new Gtk.Window ();

style_context = window.get_style_context ();
style_context.add_class ("csd");
style_context.add_class ("unified");
}

public override bool button_release_event (Clutter.ButtonEvent event) {
close_menu ();
return true;
}

#if HAS_MUTTER45
public override bool key_press_event (Clutter.Event event) {
#else
public override bool key_press_event (Clutter.KeyEvent event) {
#endif
switch (event.get_key_symbol ()) {
case Clutter.Key.Up:
cycle_menuitems (true);
return Clutter.EVENT_STOP;
case Clutter.Key.Down:
cycle_menuitems (false);
return Clutter.EVENT_STOP;
case Clutter.Key.Escape:
close_menu ();
return Clutter.EVENT_PROPAGATE;
case Clutter.Key.Return:
if (selected != null) {
selected.activated ();
}
close_menu ();
return Clutter.EVENT_PROPAGATE;
}

return Clutter.EVENT_PROPAGATE;
}

private void cycle_menuitems (bool backwards) {
Clutter.Actor child;
if (selected != null) {
if (backwards) {
child = selected.get_previous_sibling () != null ? selected.get_previous_sibling () : container.last_child;
} else {
child = selected.get_next_sibling () != null ? selected.get_next_sibling () : container.first_child;
}
} else {
child = backwards ? container.last_child : container.first_child;
}

while (child != null) {
if (child is MenuItem) {
selected = (MenuItem) child;
break;
}

child = backwards ? child.get_previous_sibling () : child.get_next_sibling ();
}
}
}