From e093207eea6383f6371f3931291f0f687fbb79da Mon Sep 17 00:00:00 2001 From: JosephMcc Date: Mon, 23 Dec 2024 11:28:35 -0800 Subject: [PATCH 01/16] menu: Redesign applet --- .../cinnamon-sass/widgets/_startmenu.scss | 98 ++- .../applets/menu@cinnamon.org/applet.js | 738 +++++++++++------- .../menu@cinnamon.org/settings-schema.json | 41 +- 3 files changed, 544 insertions(+), 333 deletions(-) diff --git a/data/theme/cinnamon-sass/widgets/_startmenu.scss b/data/theme/cinnamon-sass/widgets/_startmenu.scss index c2e3cc5a44..2fb695f392 100644 --- a/data/theme/cinnamon-sass/widgets/_startmenu.scss +++ b/data/theme/cinnamon-sass/widgets/_startmenu.scss @@ -10,33 +10,86 @@ $menu_outer_border_radius: $base_border_radius * 1.25; .menu { // this can be used to color the entire menu background but we aren't using it here - &-background {} + &-background { + // using this to override the default padding on menus so widgets can span edge + // to edge. The side effect is that some of our internal container widgets + // will need to have a border-radius added to them or the corners will poke out + // the edges of the menu. + .popup-menu-content { padding: 0; } + } + + &-sidebar { + background-color: $base_color; + border-radius: $menu_outer_border_radius 0 0 $menu_outer_border_radius; + border-right-width: 1px; + border-color: $borders_color; + + .sidebar-user-box { + padding-top: $menu_outer_padding; + padding-bottom: $menu_outer_padding / 2; + } + + StScrollBar StButton#vhandle { + // add some extra spacing at the top to align this with the separator + margin-top: $menu_outer_padding; + } - &-favorites-box { - padding: $base_padding * 1.5; - background-color: $light_bg_color; - border: 1px solid transparentize(black, 0.9); - border-radius: $base_border_radius; + .menu-favorites-button { + @extend %start_menu_button; + + padding: 3px $base_padding 3px $base_padding; + margin: 0 $menu_outer_padding 0 $menu_outer_padding; + + &:hover { + background-color: $lighter_bg_color; + border-color: $borders_color; + } + } + + .popup-separator-menu-item { + -gradient-height: 1px; + -gradient-start: lighten($base_color, 5%); + -gradient-end: lighten($base_color, 5%); + -margin-horizontal: 0; + height: 1px; + } } - &-favorites-button { - padding: $base_padding * 1.5; - border-radius: $base_border_radius; + &-bottom-box { + padding: $menu_outer_padding; + border-radius: 0 0 $menu_outer_border_radius 0; + border-top-width: 1px; + border-color: $borders_color; + } + + &-system-buttons-box { + spacing: 12px; + + .system-button { + padding: $base_padding * 1.5; + border: 1px solid $borders_color; + border-radius: 9999px; + background-color: lighten($bg_color, 10%); - &:hover { background-color: $lightest_bg_color; } + &:hover { background-color: lighten($bg_color, 15%); } + } } - &-categories-box { padding: $menu_outer_padding $base_padding * 4; } + &-categories-box { padding: $menu_outer_padding $base_padding * 3; } &-applications-box { padding: $menu_outer_padding; } - &-applications-inner-box { - &:ltr { padding-left: $base_padding * 4; } - &:rtl { padding-right: $base_padding * 4; } - } + // This small amount of padding is needed to be able + // to resize the menu with the mouse + &-applications-scrollbox { padding-right: 3px; } &-application-button { @extend %start_menu_button; &-label { padding: 0; } + &-description { + @extend %caption; + + color: $light_text_color; + } &:highlighted { font-weight: bold; } @@ -73,22 +126,17 @@ $menu_outer_border_radius: $base_border_radius * 1.25; &-context-menu {} - &-selected-app-box { - padding-right: $base_padding * 4; - padding-left: $base_padding * 4; - text-align: right; - } - - &-selected-app-title { @extend %heading; } - &-selected-app-description { color: $insensitive_fg_color; } - &-search-box { - padding: 0 0 $base_padding $menu_outer_padding; + padding: $base_padding $base_padding $base_padding $menu_outer_padding; + border-bottom-width: 1px; + border-color: $borders_color; } } #menu-search-entry { @extend %entry; + + margin: $menu_outer_padding; } .menu-search-entry-icon { diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 0afcbaf73e..3921614e55 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -1,17 +1,18 @@ const Applet = imports.ui.applet; const Mainloop = imports.mainloop; -const Lang = imports.lang; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; const Clutter = imports.gi.Clutter; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const PopupMenu = imports.ui.popupMenu; +const UserWidget = imports.ui.userWidget; const AppFavorites = imports.ui.appFavorites; const Gtk = imports.gi.Gtk; const Atk = imports.gi.Atk; const Gio = imports.gi.Gio; const XApp = imports.gi.XApp; +const AccountsService = imports.gi.AccountsService; const GnomeSession = imports.misc.gnomeSession; const ScreenSaver = imports.misc.screenSaver; const FileUtils = imports.misc.fileUtils; @@ -90,7 +91,7 @@ class VisibleChildIterator { reloadVisible() { this.array = this.container.get_focus_chain() - .filter(x => !(x._delegate instanceof PopupMenu.PopupSeparatorMenuItem)); + .filter(x => !(x._delegate instanceof PopupMenu.PopupSeparatorMenuItem)); } getNextVisible(curChild) { @@ -177,15 +178,23 @@ class SimpleMenuItem { params = Params.parse(params, SMI_DEFAULT_PARAMS, true); this._signals = new SignalManager.SignalManager(); - this.actor = new St.BoxLayout({ style_class: params.styleClass, - reactive: params.reactive, - accessible_role: Atk.Role.MENU_ITEM }); + this.actor = new St.BoxLayout({ + style_class: params.styleClass, + reactive: params.reactive, + accessible_role: Atk.Role.MENU_ITEM, + }); this._signals.connect(this.actor, 'destroy', () => this.destroy(true)); this.actor._delegate = this; this.applet = applet; + this.labelContainer = new St.BoxLayout({ + vertical: true, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); this.label = null; + this.descriptionLabel = null; this.icon = null; this.matchIndex = NO_MATCH; @@ -197,8 +206,8 @@ class SimpleMenuItem { this._signals.connect(this.actor, 'enter-event', () => applet._buttonEnterEvent(this)); this._signals.connect(this.actor, 'leave-event', () => applet._buttonLeaveEvent(this)); if (params.activatable || params.withMenu) { - this._signals.connect(this.actor, 'button-release-event', Lang.bind(this, this._onButtonReleaseEvent)); - this._signals.connect(this.actor, 'key-press-event', Lang.bind(this, this._onKeyPressEvent)); + this._signals.connect(this.actor, 'button-release-event', this._onButtonReleaseEvent.bind(this)); + this._signals.connect(this.actor, 'key-press-event', this._onKeyPressEvent.bind(this)); } } } @@ -278,11 +287,12 @@ class SimpleMenuItem { if (this.label) return; - this.label = new St.Label({ text: label, y_expand: true, y_align: Clutter.ActorAlign.CENTER }); + this.label = new St.Label({ text: label }); if (styleClass) this.label.set_style_class_name(styleClass); this.label.clutter_text.ellipsize = Pango.EllipsizeMode.END; - this.actor.add_actor(this.label); + this.actor.add_actor(this.labelContainer); + this.labelContainer.add_actor(this.label); } /** @@ -295,6 +305,25 @@ class SimpleMenuItem { this.label = null; } + addDescription(label='', styleClass=null) { + if (this.descriptionLabel) + return; + + if (label === '') + return; + + this.descriptionLabel = new St.Label({ + text: label, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER + }); + + if (styleClass) + this.descriptionLabel.set_style_class_name(styleClass); + this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END; + this.labelContainer.add_actor(this.descriptionLabel); + } + /** * Adds a ClutterActor as the next child. * @@ -318,6 +347,8 @@ class SimpleMenuItem { if (this.label) this.label.destroy(); + if (this.descriptionLabel) + this.descriptionLabel.destroy(); if (this.icon) this.icon.destroy(); if (!actorDestroySignal) @@ -326,6 +357,7 @@ class SimpleMenuItem { delete this.actor._delegate; delete this.actor; delete this.label; + delete this.descriptionLabel; delete this.icon; } } @@ -339,7 +371,11 @@ class ApplicationContextMenuItem extends PopupMenu.PopupBaseMenuItem { this.label = new St.Label({ text: label }); if (iconName != null) { - this.icon = new St.Icon({ icon_name: iconName, icon_size: 12, icon_type: St.IconType.SYMBOLIC }); + this.icon = new St.Icon({ + icon_name: iconName, + icon_size: 12, + icon_type: St.IconType.SYMBOLIC, + }); if (this.icon) this.addActor(this.icon); } @@ -415,12 +451,14 @@ class ApplicationContextMenuItem extends PopupMenu.PopupBaseMenuItem { class GenericApplicationButton extends SimpleMenuItem { constructor(applet, app, type, withMenu=false, styleClass="") { let desc = app.get_description() || ""; - super(applet, { name: app.get_name(), - description: desc.split("\n")[0], - type: type, - withMenu: withMenu, - styleClass: styleClass, - app: app }); + super(applet, { + name: app.get_name(), + description: desc.split("\n")[0], + type: type, + withMenu: withMenu, + styleClass: styleClass, + app: app, + }); } highlight() { @@ -479,7 +517,7 @@ class GenericApplicationButton extends SimpleMenuItem { menuItem = new ApplicationContextMenuItem(this, _("Uninstall"), "uninstall", "edit-delete"); menu.addMenuItem(menuItem); } - + let actions = appinfo.list_actions(); if (actions) { for (let i = 0; i < actions.length; i++) { @@ -494,9 +532,12 @@ class GenericApplicationButton extends SimpleMenuItem { class TransientButton extends SimpleMenuItem { constructor(applet, pathOrCommand) { - super(applet, { description: pathOrCommand, - type: 'transient', - styleClass: 'menu-application-button' }); + super(applet, { + description: pathOrCommand, + type: 'transient', + styleClass: 'menu-application-button', + }); + if (pathOrCommand.startsWith('~')) { pathOrCommand = pathOrCommand.slice(1); pathOrCommand = GLib.get_home_dir() + pathOrCommand; @@ -521,11 +562,19 @@ class TransientButton extends SimpleMenuItem { this.handler = this.file.query_default_handler(null); let contentType = Gio.content_type_guess(this.pathOrCommand, null); let themedIcon = Gio.content_type_get_icon(contentType[0]); - this.icon = new St.Icon({gicon: themedIcon, icon_size: applet.applicationIconSize, icon_type: St.IconType.FULLCOLOR }); + this.icon = new St.Icon({ + gicon: themedIcon, + icon_size: applet.applicationIconSize, + icon_type: St.IconType.FULLCOLOR, + }); } catch (e) { this.handler = null; let iconName = this.isPath ? 'folder' : 'unknown'; - this.icon = new St.Icon({icon_name: iconName, icon_size: applet.applicationIconSize, icon_type: St.IconType.FULLCOLOR}); + this.icon = new St.Icon({ + icon_name: iconName, + icon_size: applet.applicationIconSize, + icon_type: St.IconType.FULLCOLOR, + }); // @todo Would be nice to indicate we don't have a handler for this file. } @@ -564,9 +613,11 @@ class ApplicationButton extends GenericApplicationButton { this.icon.visible = false; this.addLabel(this.name, 'menu-application-button-label'); + if (applet.showDescription) + this.addDescription(this.description, 'menu-application-button-description'); this._draggable = DND.makeDraggable(this.actor); - this._signals.connect(this._draggable, 'drag-end', Lang.bind(this, this._onDragEnd)); + this._signals.connect(this._draggable, 'drag-end', this._onDragEnd.bind(this)); this.isDraggableApp = true; this.searchStrings = [ @@ -582,7 +633,7 @@ class ApplicationButton extends GenericApplicationButton { } getDragActor() { - return this.app.create_icon_texture(this.applet.favIconSize); + return this.app.create_icon_texture(this.applet.sidebarIconSize); } // Returns the original actor that should align with the actor @@ -592,8 +643,9 @@ class ApplicationButton extends GenericApplicationButton { } _onDragEnd() { - this.applet.favoritesBox._delegate._clearDragPlaceholder(); + this.applet.favoriteAppsBox._delegate._clearDragPlaceholder(); } + destroy() { delete this._draggable; super.destroy(); @@ -602,12 +654,14 @@ class ApplicationButton extends GenericApplicationButton { class SearchProviderResultButton extends SimpleMenuItem { constructor(applet, provider, result) { - super(applet, { name:result.label, - description: result.description, - type: 'search-provider', - styleClass: 'menu-application-button', - provider: provider, - result: result }); + super(applet, { + name:result.label, + description: result.description, + type: 'search-provider', + styleClass: 'menu-application-button', + provider: provider, + result: result, + }); if (applet.showApplicationIcons) { if (result.icon) { @@ -615,7 +669,10 @@ class SearchProviderResultButton extends SimpleMenuItem { } else if (result.icon_app) { this.icon = result.icon_app.create_icon_texture(applet.applicationIconSize); } else if (result.icon_filename) { - this.icon = new St.Icon({gicon: new Gio.FileIcon({file: Gio.file_new_for_path(result.icon_filename)}), icon_size: applet.applicationIconSize}); + this.icon = new St.Icon({ + gicon: new Gio.FileIcon({ file: Gio.file_new_for_path(result.icon_filename) }), + icon_size: applet.applicationIconSize, + }); } if (this.icon) @@ -652,13 +709,14 @@ class PlaceButton extends SimpleMenuItem { selectedAppId = place.name } - super(applet, { name: place.name, - description: selectedAppId, - type: 'place', - styleClass: 'menu-application-button', - place: place }); + super(applet, { + name: place.name, + type: 'place', + styleClass: 'menu-favorites-button', + place: place, + }); - this.icon = place.iconFactory(applet.applicationIconSize); + this.icon = place.iconFactory(applet.sidebarIconSize); if (this.icon) this.addActor(this.icon); else @@ -668,10 +726,6 @@ class PlaceButton extends SimpleMenuItem { this.icon.visible = false; this.addLabel(this.name, 'menu-application-button-label'); - - this.searchStrings = [ - AppUtils.decomp_string(place.name).replace(/\s/g, '') - ]; } activate() { @@ -705,14 +759,16 @@ class RecentButton extends SimpleMenuItem { let fileIndex = recent.uriDecoded.indexOf("file:///"); let selectedAppUri = fileIndex === -1 ? "" : recent.uriDecoded.substr(fileIndex + 7); - super(applet, { name: recent.name, - description: selectedAppUri, - type: 'recent', - styleClass: 'menu-application-button', - withMenu: true, - mimeType: recent.mimeType, - uri: recent.uri, - uriDecoded: recent.uriDecoded }); + super(applet, { + name: recent.name, + description: selectedAppUri, + type: 'recent', + styleClass: 'menu-application-button', + withMenu: true, + mimeType: recent.mimeType, + uri: recent.uri, + uriDecoded: recent.uriDecoded, + }); this.icon = recent.createIcon(applet.applicationIconSize); this.addActor(this.icon); @@ -720,6 +776,8 @@ class RecentButton extends SimpleMenuItem { this.icon.visible = false; this.addLabel(this.name, 'menu-application-button-label'); + if (applet.showDescription) + this.addDescription(this.description, 'menu-application-button-description'); this.searchStrings = [ AppUtils.decomp_string(recent.name).replace(/\s/g, '') @@ -734,8 +792,8 @@ class RecentButton extends SimpleMenuItem { let source = new MessageTray.SystemNotificationSource(); Main.messageTray.add(source); let notification = new MessageTray.Notification(source, - _("This file is no longer available"), - e.message); + _("This file is no longer available"), + e.message); notification.setTransient(true); notification.setUrgency(MessageTray.Urgency.NORMAL); source.notify(notification); @@ -809,18 +867,23 @@ class RecentButton extends SimpleMenuItem { class FavoriteButton extends SimpleMenuItem { constructor(applet, favoriteInfo) { - super(applet, { name: favoriteInfo.display_name, - description: decodeURIComponent(favoriteInfo.uri), - type: 'favorite', - styleClass: 'menu-application-button', - withMenu: true, - mimeType: favoriteInfo.cached_mimetype, - uri: favoriteInfo.uri }); + super(applet, { + name: favoriteInfo.display_name, + description: decodeURIComponent(favoriteInfo.uri), + type: 'favorite', + styleClass: 'menu-application-button', + withMenu: true, + mimeType: favoriteInfo.cached_mimetype, + uri: favoriteInfo.uri, + }); this.favoriteInfo = favoriteInfo; let gicon = Gio.content_type_get_icon(favoriteInfo.cached_mimetype); - this.icon = new St.Icon({ gicon: gicon, icon_size: applet.applicationIconSize }) + this.icon = new St.Icon({ + gicon: gicon, + icon_size: applet.applicationIconSize, + }); this.addActor(this.icon); @@ -842,8 +905,8 @@ class FavoriteButton extends SimpleMenuItem { let source = new MessageTray.SystemNotificationSource(); Main.messageTray.add(source); let notification = new MessageTray.Notification(source, - _("This file is no longer available"), - e.message); + _("This file is no longer available"), + e.message); notification.setTransient(true); notification.setUrgency(MessageTray.Urgency.NORMAL); source.notify(notification); @@ -917,10 +980,12 @@ class FavoriteButton extends SimpleMenuItem { class CategoryButton extends SimpleMenuItem { constructor(applet, categoryId, label, icon, symbolic=false) { - super(applet, { name: label, - type: 'category', - styleClass: 'menu-category-button', - categoryId: categoryId }); + super(applet, { + name: label, + type: 'category', + styleClass: 'menu-category-button', + categoryId: categoryId, + }); this.actor.accessible_role = Atk.Role.LIST_ITEM; if (typeof icon === 'string') @@ -941,7 +1006,7 @@ class CategoryButton extends SimpleMenuItem { return; this.applet._select_category(this.categoryId); this.applet._previousSelectedAppActor = null; - this.applet.categoriesBox.get_children().forEach(child => + this.applet.categoriesBox.get_children().forEach(child => child.set_style_class_name("menu-category-button")); this.actor.style_class = "menu-category-button-selected"; } @@ -950,11 +1015,12 @@ class CategoryButton extends SimpleMenuItem { class FavoritesButton extends GenericApplicationButton { constructor(applet, app) { super(applet, app, 'fav', false, 'menu-favorites-button'); - this.icon = app.create_icon_texture(applet.favIconSize); + this.icon = app.create_icon_texture(applet.sidebarIconSize); this.addActor(this.icon); + this.addLabel(this.name); this._draggable = DND.makeDraggable(this.actor); - this._signals.connect(this._draggable, 'drag-end', Lang.bind(this, this._onDragEnd)); + this._signals.connect(this._draggable, 'drag-end', this._onDragEnd.bind(this)); this.isDraggableApp = true; } @@ -984,11 +1050,13 @@ class FavoritesButton extends GenericApplicationButton { class SystemButton extends SimpleMenuItem { constructor(applet, iconName, name, desc) { - super(applet, { name: name, - description: desc, - type: 'system', - styleClass: 'menu-favorites-button' }); - this.addIcon(applet.favIconSize, iconName); + super(applet, { + name: name, + description: desc, + type: 'system', + styleClass: 'system-button', + }); + this.addIcon(16, iconName, null, true); } } @@ -1016,9 +1084,24 @@ class CategoriesApplicationsBox { } } -class FavoritesBox { +class SidebarAppsBox { constructor() { - this.actor = new St.BoxLayout({ vertical: true, y_expand: true, y_align: Clutter.ActorAlign.START }); + this.actor = new St.BoxLayout({ + vertical: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + }); + this.actor._delegate = this; + } +} + +class FavoriteAppsBox { + constructor() { + this.actor = new St.BoxLayout({ + vertical: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + }); this.actor._delegate = this; this._dragPlaceholder = null; @@ -1071,10 +1154,9 @@ class FavoritesBox { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); this._animatingPlaceholdersCount++; - this._dragPlaceholder.actor.connect('destroy', - Lang.bind(this, function() { - this._animatingPlaceholdersCount--; - })); + this._dragPlaceholder.actor.connect('destroy', () => { + this._animatingPlaceholdersCount--; + }); } this._dragPlaceholder = null; @@ -1137,15 +1219,14 @@ class FavoritesBox { favPos++; } - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, - function () { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { let appFavorites = AppFavorites.getAppFavorites(); if (srcIsFavorite) appFavorites.moveFavoriteToPos(id, favPos); else appFavorites.addFavoriteAtPos(id, favPos); return false; - })); + }); return true; } @@ -1199,7 +1280,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { () => this.popup_height * global.ui_scale); this.settings.bind("show-favorites", "showFavorites", () => this.queueRefresh(RefreshFlags.FAV_DOC)); - this.settings.bind("show-places", "showPlaces", () => this.queueRefresh(RefreshFlags.PLACE)); this.settings.bind("show-recents", "showRecents", () => this.queueRefresh(RefreshFlags.RECENT)); this._appletEnterEventId = 0; @@ -1211,7 +1291,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._updateActivateOnHover(); this.menu.setCustomStyleClass('menu-background'); - this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); + this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); this.menu.connect('menu-animated-closed', () => { this._clearAllSelections(); this._hideAllAppActors(); @@ -1223,27 +1303,33 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("menu-label", "menuLabel", this._updateIconAndLabel); this.settings.bind("overlay-key", "overlayKey", this._updateKeybinding); this.settings.bind("show-category-icons", "showCategoryIcons", () => this._updateShowIcons(this.categoriesBox, this.showCategoryIcons)); - this.settings.bind("category-icon-size", "categoryIconSize", () => this.queueRefresh(RefreshFlags.PLACE | RefreshFlags.RECENT | RefreshFlags.APP)); + this.settings.bind("category-icon-size", "categoryIconSize", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); this.settings.bind("show-application-icons", "showApplicationIcons", () => this._updateShowIcons(this.applicationsBox, this.showApplicationIcons)); this.settings.bind("category-hover", "categoryHover", this._updateCategoryHover); - this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(RefreshFlags.PLACE | RefreshFlags.RECENT | RefreshFlags.APP)); - this.settings.bind("favbox-show", "favBoxShow", this._favboxtoggle); - this.settings.bind("fav-icon-size", "favIconSize", () => this.queueRefresh(RefreshFlags.FAV_APP | RefreshFlags.SYSTEM)); + this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); + this.settings.bind("show-description", "showDescription", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); + this.settings.bind("sidebar-show", "sidebarShow", this._sidebarToggle); + this.settings.bind("sidebar-icon-size", "sidebarIconSize", () => this.queueRefresh(RefreshFlags.FAV_APP | RefreshFlags.PLACE)); + this.settings.bind("bottombox-show", "bottomBoxShow", this._bottomBoxToggle); this.settings.bind("enable-animation", "enableAnimation", null); this.settings.bind("popup-width", "popup_width"); this.settings.bind("popup-height", "popup_height"); this._updateKeybinding(); - Main.themeManager.connect("theme-set", Lang.bind(this, this._theme_set)); + Main.themeManager.connect("theme-set", this._theme_set.bind(this)); this._updateIconAndLabel(); - this._searchInactiveIcon = new St.Icon({ style_class: 'menu-search-entry-icon', + this._searchInactiveIcon = new St.Icon({ + style_class: 'menu-search-entry-icon', icon_name: 'edit-find', - icon_type: St.IconType.SYMBOLIC }); - this._searchActiveIcon = new St.Icon({ style_class: 'menu-search-entry-icon', + icon_type: St.IconType.SYMBOLIC, + }); + this._searchActiveIcon = new St.Icon({ + style_class: 'menu-search-entry-icon', icon_name: 'edit-clear', - icon_type: St.IconType.SYMBOLIC }); + icon_type: St.IconType.SYMBOLIC, + }); this._searchIconClickedId = 0; this._applicationsButtons = []; this._favoriteAppButtons = []; @@ -1321,14 +1407,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _updateKeybinding() { - Main.keybindingManager.addHotKey("overlay-key-" + this.instance_id, this.overlayKey, Lang.bind(this, function() { + Main.keybindingManager.addHotKey("overlay-key-" + this.instance_id, this.overlayKey, () => { if (!Main.overview.visible && !Main.expo.visible) { if (this.forceShowPanel && !this.isOpen) { this.panel.peekPanel(); } this.menu.toggle_with_options(this.enableAnimation); } - })); + }); } _updateCategoryHover() { @@ -1339,7 +1425,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } if (this.categoryHover) { - child._delegate.actor_motion_id = child.connect("motion-event", Lang.bind(this, this._categoryMotionEvent)); + child._delegate.actor_motion_id = child.connect("motion-event", this._categoryMotionEvent.bind(this)); } }, this); } @@ -1379,10 +1465,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesBox.set_child_at_index(this.favoriteDocsButton.actor, -1); } - if (this.placesButton) { - this.categoriesBox.set_child_at_index(this.placesButton.actor, -1); - } - if (this.recentButton) { this.categoriesBox.set_child_at_index(this.recentButton.actor, -1); } @@ -1440,7 +1522,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.main_container.natural_height = (height * global.ui_scale); this.main_container.natural_width = (width * global.ui_scale); - this._update_scroll_policy(this.favoritesBox, this.favoritesScrollBox); + this._update_scroll_policy(this.sidebarAppsBox, this.sidebarAppsScrollBox); this._update_scroll_policy(this.categoriesBox, this.categoriesScrollBox); this._size_dirty = false; @@ -1477,7 +1559,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._setMenuSize(this.popup_width, this.popup_height); } - // on_applet_clicked(event) { this.menu.toggle_with_options(this.enableAnimation); @@ -1503,31 +1584,26 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; - Mainloop.idle_add(Lang.bind(this, this._initial_cat_selection, n)); + Mainloop.idle_add(() => { + if(this.lastSelectedCategory !== null) //if a category is already selected + return; + for (let i = n; i < this._applicationsButtons.length; i++) + this._applicationsButtons[i].actor.show(); + }); } else { this.actor.remove_style_pseudo_class('active'); if (this.searchActive) { this.resetSearch(); } - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); + this._previousCategoryHoverActor = null; this._previousSelectedAppActor = null; this.closeContextMenu(false); - + this._disableVectorMask(); this._scrollToButton(null, this.applicationsScrollBox); this._scrollToButton(null, this.categoriesScrollBox); - this._scrollToButton(null, this.favoritesScrollBox); - } - } - - _initial_cat_selection (start_index) { - if(this.lastSelectedCategory !== null) //if a category is already selected - return; - let n = this._applicationsButtons.length; - for (let i = start_index; i < n; i++) { - this._applicationsButtons[i].actor.show(); + this._scrollToButton(null, this.sidebarAppsScrollBox); } } @@ -1538,12 +1614,18 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.emit('destroy'); } - _favboxtoggle() { - if (!this.favBoxShow) { - this.left_box.hide(); - } else { - this.left_box.show(); - } + _sidebarToggle() { + if (!this.sidebarShow) + this.sidebar.hide(); + else + this.sidebar.show(); + } + + _bottomBoxToggle() { + if (!this.bottomBoxShow) + this.menuBottomBox.hide(); + else + this.menuBottomBox.show(); } // Override js/applet.js so _updateIconAndLabel doesn't have to fight with size changes @@ -1657,7 +1739,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { }; menu.actor.set_style_class_name('menu-context-menu'); - menu.connect('open-state-changed', Lang.bind(this, this._contextMenuOpenStateChanged)); + menu.connect('open-state-changed', this._contextMenuOpenStateChanged.bind(this)); this.contextMenu = menu; this.applicationsBox.add_actor(menu.actor); } else if (this.contextMenu.isOpen && @@ -1745,6 +1827,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let item_actor; this.appBoxIter.reloadVisible(); this.catBoxIter.reloadVisible(); + this.placesBoxIter.reloadVisible(); this.favBoxIter.reloadVisible(); this.sysBoxIter.reloadVisible(); @@ -1755,19 +1838,19 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { Switches between left/right key presses */ if(St.Widget.get_default_direction() === St.TextDirection.RTL) { switch(symbol) { - case Clutter.KEY_Right: + case Clutter.KEY_Right: symbol = Clutter.KEY_Left; - break; - case Clutter.KEY_KP_Right: - symbol = Clutter.KEY_RP_Left; - break; - case Clutter.KEY_Left: - symbol = Clutter.KEY_Right; - break; + break; + case Clutter.KEY_KP_Right: + symbol = Clutter.KEY_RP_Left; + break; + case Clutter.KEY_Left: + symbol = Clutter.KEY_Right; + break; case Clutter.KEY_KP_Left: - symbol = Clutter.KEY_KP_Right; - break; - } + symbol = Clutter.KEY_KP_Right; + break; + } } /* check for a keybinding and quit early, otherwise we get a double hit @@ -1813,21 +1896,21 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let navigationKey = true; let whichWay = "none"; if (this._activeContainer === null) { - this._setKeyFocusToCurrentCategoryButton(); + this._setKeyFocusToCurrentCategoryButton(); } - + switch (symbol) { case Clutter.KEY_Up: case Clutter.KEY_KP_Up: whichWay = "up"; - if (this._activeContainer === this.favoritesBox && ctrlKey && + if (this._activeContainer === this.favoriteAppsBox && ctrlKey && this._activeActor._delegate instanceof FavoritesButton) navigationKey = false; break; case Clutter.KEY_Down: case Clutter.KEY_KP_Down: whichWay = "down"; - if (this._activeContainer === this.favoritesBox && ctrlKey && + if (this._activeContainer === this.favoriteAppsBox && ctrlKey && this._activeActor._delegate instanceof FavoritesButton) navigationKey = false; break; @@ -1849,9 +1932,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { case Clutter.KEY_KP_Left: if (!this.searchActive) whichWay = "left"; - if (this._activeContainer === this.favoritesBox || this._activeContainer === this.systemButtonsBox) + if (this._activeContainer === this.favoriteAppsBox || this._activeContainer === this.placesBox) whichWay = "none"; - else if (!this.favBoxShow && this._activeContainer === this.categoriesBox) + else if (!this.placesBox && this._activeContainer === this.categoriesBox) whichWay = "none"; break; case Clutter.KEY_Tab: @@ -1875,16 +1958,27 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { case this.categoriesBox: switch (whichWay) { case "up": - item_actor = this.catBoxIter.getPrevVisible(this._activeActor); + if (this._activeActor === this.catBoxIter.getFirstVisible()) { + item_actor = this.catBoxIter.getLastVisible(); + } else { + item_actor = this.catBoxIter.getPrevVisible(this._activeActor); + } break; case "down": - item_actor = this.catBoxIter.getNextVisible(this._activeActor); + if (this._activeActor === this.catBoxIter.getLastVisible()) { + if (this.bottomBoxShow) + item_actor = this.sysBoxIter.getFirstVisible(); + else + item_actor = this.catBoxIter.getFirstVisible(); + } else { + item_actor = this.catBoxIter.getNextVisible(this._activeActor); + } break; case "right": if (this._activeActor._delegate.categoryId === "recent" && this.noRecentDocuments) { - if(this.favBoxShow) { - item_actor = this.favBoxIter.getFirstVisible(); + if(this.sidebarShow) { + item_actor = this.placesBoxIter.getFirstVisible(); } } else { item_actor = (this._previousSelectedAppActor != null) ? @@ -1892,8 +1986,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } break; case "left": - if(this.favBoxShow) { - item_actor = this.favBoxIter.getFirstVisible(); + if (this.placesBoxIter) { + item_actor = this.placesBoxIter.getFirstVisible(); } else if (this._activeActor._delegate.categoryId != "recent" || !this.noRecentDocuments) { item_actor = (this._previousSelectedAppActor != null) ? @@ -1918,8 +2012,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { item_actor = this.appBoxIter.getNextVisible(this._activeActor); break; case "right": - if (this.favBoxShow) { - item_actor = this.favBoxIter.getFirstVisible(); + if (this.sidebarShow) { + item_actor = this.placesBoxIter.getFirstVisible(); } else { item_actor = (this._previousCategoryHoverActor != null) ? this._previousCategoryHoverActor : @@ -1939,18 +2033,18 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { break; } break; - case this.favoritesBox: + case this.favoriteAppsBox: switch (whichWay) { case "up": if (this._activeActor === this.favBoxIter.getFirstVisible()) { - item_actor = this.sysBoxIter.getLastVisible(); + item_actor = this.placesBoxIter.getLastVisible(); } else { item_actor = this.favBoxIter.getPrevVisible(this._activeActor); } break; case "down": if (this._activeActor === this.favBoxIter.getLastVisible()) { - item_actor = this.sysBoxIter.getFirstVisible(); + item_actor = this.placesBoxIter.getFirstVisible(); } else { item_actor = this.favBoxIter.getNextVisible(this._activeActor); } @@ -1973,27 +2067,27 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.appBoxIter.getFirstVisible(); break; case "top": - item_actor = this.favBoxIter.getFirstVisible(); + item_actor = this.placesBoxIter.getFirstVisible(); break; case "bottom": item_actor = this.favBoxIter.getLastVisible(); break; } break; - case this.systemButtonsBox: + case this.placesBox: switch (whichWay) { case "up": - if (this._activeActor === this.sysBoxIter.getFirstVisible()) { + if (this._activeActor === this.placesBoxIter.getFirstVisible()) { item_actor = this.favBoxIter.getLastVisible(); } else { - item_actor = this.sysBoxIter.getPrevVisible(this._activeActor); + item_actor = this.placesBoxIter.getPrevVisible(this._activeActor); } break; case "down": - if (this._activeActor === this.sysBoxIter.getLastVisible()) { + if (this._activeActor === this.placesBoxIter.getLastVisible()) { item_actor = this.favBoxIter.getFirstVisible(); } else { - item_actor = this.sysBoxIter.getNextVisible(this._activeActor); + item_actor = this.placesBoxIter.getNextVisible(this._activeActor); } break; case "right": @@ -2013,6 +2107,33 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._previousSelectedAppActor : this.appBoxIter.getFirstVisible(); break; + case "top": + item_actor = this.placesBoxIter.getFirstVisible(); + break; + case "bottom": + item_actor = this.favBoxIter.getLastVisible(); + break; + } + break; + case this.systemButtonsBox: + switch (whichWay) { + case "up": + item_actor = this.catBoxIter.getLastVisible(); + break; + case "right": + if (this._activeActor === this.sysBoxIter.getLastVisible()) { + item_actor = this.sysBoxIter.getFirstVisible(); + } else { + item_actor = this.sysBoxIter.getNextVisible(this._activeActor); + } + break; + case "left": + if (this._activeActor === this.sysBoxIter.getFirstVisible()) { + item_actor = this.sysBoxIter.getLastVisible(); + } else { + item_actor = this.sysBoxIter.getPrevVisible(this._activeActor); + } + break; case "top": item_actor = this.sysBoxIter.getFirstVisible(); break; @@ -2037,7 +2158,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } else if (this._activeContainer === this.applicationsBox && symbol === Clutter.KEY_Menu) { this.toggleContextMenu(this._activeActor._delegate); return true; - } else if (!this.searchActive && this._activeContainer === this.favoritesBox && symbol === Clutter.KEY_Delete) { + } else if (!this.searchActive && this._activeContainer === this.favoriteAppsBox && symbol === Clutter.KEY_Delete) { item_actor = this._activeActor; const selectedItemIndex = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(this._activeActor); if (item_actor._delegate instanceof FavoritesButton) { @@ -2045,13 +2166,13 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let numFavorites = favorites.length; AppFavorites.getAppFavorites().removeFavorite(item_actor._delegate.app.get_id()); if (selectedItemIndex == (numFavorites-1)) - item_actor = this.favoritesBox.get_child_at_index(selectedItemIndex-1); + item_actor = this.favoriteAppsBox.get_child_at_index(selectedItemIndex-1); else - item_actor = this.favoritesBox.get_child_at_index(selectedItemIndex); + item_actor = this.favoriteAppsBox.get_child_at_index(selectedItemIndex); } - } else if (this._activeContainer === this.favoritesBox && - (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down || - symbol === Clutter.KEY_Up || symbol === Clutter.KEY_KP_Up) && + } else if (this._activeContainer === this.favoriteAppsBox && + (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down || + symbol === Clutter.KEY_Up || symbol === Clutter.KEY_KP_Up) && ctrlKey && this._activeActor._delegate instanceof FavoritesButton) { const selectedItemIndex = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(this._activeActor); item_actor = this._activeActor; @@ -2069,7 +2190,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { else favPos = selectedItemIndex - 1; appFavorites.moveFavoriteToPos(id, favPos); - item_actor = this.favoritesBox.get_child_at_index(favPos); + item_actor = this.favoriteAppsBox.get_child_at_index(favPos); } else if (this.searchFilesystem && (this._fileFolderAccessActive || symbol === Clutter.KEY_slash)) { if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) { if (this._run(this.searchEntry.get_text())) { @@ -2121,9 +2242,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } } - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); - if (!item_actor || item_actor === this.searchEntry) { return false; } @@ -2131,11 +2249,11 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (item_actor._delegate instanceof CategoryButton) { this._scrollToButton(item_actor._delegate, this.categoriesScrollBox); } else if (item_actor._delegate instanceof FavoritesButton) { - this._scrollToButton(item_actor._delegate, this.favoritesScrollBox); + this._scrollToButton(item_actor._delegate, this.sidebarAppsScrollBox); } else if (item_actor.get_parent() === this.applicationsBox) { this._scrollToButton(item_actor._delegate, this.applicationsScrollBox); } - + this._buttonEnterEvent(item_actor._delegate); return true; } @@ -2143,16 +2261,17 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _buttonEnterEvent(button) { this.categoriesBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); this.applicationsBox.get_children().forEach(child => child.set_style_class_name("menu-application-button")); - this.favoritesBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); + this.favoriteAppsBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); + this.placesBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); this.systemButtonsBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); - + if (button instanceof CategoryButton) { if (this.searchActive) return; - + if (button.categoryId !== this.lastSelectedCategory) { if (this.categoryHover) { - this.categoriesBox.get_children().forEach(child => + this.categoriesBox.get_children().forEach(child => child.set_style_class_name("menu-category-button")); button.activate(); } else { @@ -2161,15 +2280,13 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } this._previousCategoryHoverActor = button.actor; } else { - const isFav = button instanceof FavoritesButton || button instanceof SystemButton; + const isFav = button instanceof FavoritesButton || button instanceof SystemButton || button instanceof PlaceButton; if (isFav) { button.actor.add_style_pseudo_class("hover"); } else { button.actor.set_style_class_name(`${button.styleClass}-selected`); this._previousSelectedAppActor = button.actor; } - this.selectedAppTitle.set_text(button.name); - this.selectedAppDescription.set_text(button.description); } @@ -2187,9 +2304,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } } } else { - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); - if (button instanceof FavoritesButton || button instanceof SystemButton) button.actor.remove_style_pseudo_class("hover"); else @@ -2363,7 +2477,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { // While the mask is active, disable category buttons. this._setCategoryButtonsReactive(false); - this.vector_update_loop = Mainloop.timeout_add(CinnamonMenuApplet.POLL_INTERVAL, Lang.bind(this, this._maskPollTimeout)); + this.vector_update_loop = Mainloop.timeout_add(CinnamonMenuApplet.POLL_INTERVAL, this._maskPollTimeout.bind(this)); } _maskPollTimeout() { @@ -2405,42 +2519,21 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } } - _refreshPlaces () { - for (let i = 0; i < this._placesButtons.length; i ++) { - this._placesButtons[i].actor.destroy(); - } + _refreshPlaces() { + //Remove all places + this.placesBox.destroy_all_children(); + //Load places again this._placesButtons = []; - for (let i = 0; i < this._categoryButtons.length; i++) { - if (this._categoryButtons[i].categoryId === 'place') { - this._categoryButtons[i].destroy(); - this._categoryButtons.splice(i, 1); - this.placesButton = null; - break; - } - } - - if (!this.showPlaces) { - return; - } - - // Now generate Places category and places buttons and add to the list - if (!this.placesButton) { - this.placesButton = new CategoryButton(this, 'place', _('Places'), 'folder'); - this._categoryButtons.push(this.placesButton); - this.categoriesBox.add_actor(this.placesButton.actor); - } - - // places go after applications. we add them in reverse starting below the last ApplicationButton - let sibling = this._applicationsButtons[this._applicationsButtons.length - 1].actor; let places = Main.placesManager.getAllPlaces(); - for (let i = places.length - 1; i >= 0; i--) { + for (let i = 0; i < places.length; ++i ) { let button = new PlaceButton(this, places[i]); this._placesButtons.push(button); - this.applicationsBox.insert_child_below(button.actor, sibling); - button.actor.visible = this.menu.isOpen && this.lastSelectedCategory === "place"; - sibling = button.actor; + this.placesBox.add(button.actor, { + y_align: St.Align.END, + y_fill: false + }); } } @@ -2554,7 +2647,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { for (let i = this._categoryButtons.length - 1; i > -1; i--) { let b = this._categoryButtons[i]; if (b === this._allAppsCategoryButton || - ['place', 'recent', 'favorite'].includes(b.categoryId)) + ['recent', 'favorite'].includes(b.categoryId)) continue; this._categoryButtons[i].destroy(); this._categoryButtons.splice(i, 1); @@ -2608,9 +2701,12 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _refreshFavApps() { //Remove all favorites - this.favoritesBox.destroy_all_children(); + this.favoriteAppsBox.destroy_all_children(); + + //Load favorites again addding the separator first + let separator = new PopupMenu.PopupSeparatorMenuItem(); + this.favoriteAppsBox.add_actor(separator.actor); - //Load favorites again this._favoriteAppButtons = []; let launchers = global.settings.get_strv('favorite-apps'); for ( let i = 0; i < launchers.length; ++i ) { @@ -2618,7 +2714,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (app) { let button = new FavoritesButton(this, app); this._favoriteAppButtons[app] = button; - this.favoritesBox.add(button.actor, { y_align: St.Align.END, y_fill: false }); + this.favoriteAppsBox.add(button.actor, { y_align: St.Align.END, y_fill: false }); } } } @@ -2701,6 +2797,53 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } } + _buildSidebar() { + this.sidebar = new St.BoxLayout({ + style_class: 'menu-sidebar', + vertical: true + }); + + this.userBox = new St.BoxLayout({ + style_class: 'sidebar-user-box', + vertical: true + }); + + let user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); + let userIcon = new UserWidget.UserWidget(user, Clutter.Orientation.VERTICAL); + this.userBox.add(userIcon); + + this.sidebar.add(this.userBox, { + x_fill: true, + y_fill: true, + x_align: St.Align.MIDDLE, + y_align: St.Align.START + }); + + this.sidebar.add(new PopupMenu.PopupSeparatorMenuItem().actor); + + this.sidebarAppsBox = new SidebarAppsBox().actor; + this.sidebarAppsScrollBox = new St.ScrollView({ + y_align: St.Align.START, + style_class: 'vfade menu-sidebar-scrollbox' + }); + this.sidebarAppsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); + this.sidebarAppsScrollBox.add_actor(this.sidebarAppsBox); + + this.sidebar.add(this.sidebarAppsScrollBox, { expand: true }); + + this.placesBox = new St.BoxLayout({ + vertical: true, + y_align: Clutter.ActorAlign.START + }); + + this.sidebarAppsBox.add(this.placesBox); + + this.favoriteAppsBox = new FavoriteAppsBox().actor; + this.sidebarAppsBox.add(this.favoriteAppsBox); + + this.main_container.add(this.sidebar, { span: 1 }); + } + _display() { this._activeContainer = null; this._activeActor = null; @@ -2710,38 +2853,58 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let section = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(section); - this.main_container = new St.BoxLayout({ vertical: false, style_class: 'menu-applications-outer-box' }); - this.main_container.add_style_class_name('menu-applications-box'); //this is to support old themes + this.main_container = new St.BoxLayout({ + vertical: false, + style_class: 'menu-main-box' + }); this.main_container._delegate = null; - section.addActor(this.main_container, { expand: true, span: -1, align: St.Align.START }); + section.addActor(this.main_container, { + expand: true, + span: -1, + align: St.Align.START, + }); - this.left_box = new St.BoxLayout({ style_class: 'menu-favorites-box', vertical: true }); - this.main_container.add(this.left_box, { span: 1 }); + this._buildSidebar(); this.right_box = new St.BoxLayout({ vertical: true }); this.main_container.add(this.right_box, { expand: true }); - this.searchBox = new St.BoxLayout({ style_class: 'menu-search-box', vertical: false }); - this.searchEntry = new St.Entry({ name: 'menu-search-entry', - track_hover: true, - can_focus: true }); - this.searchBox.add(this.searchEntry, { x_align: St.Align.START, y_align: St.Align.MIDDLE, expand: true}); + this.searchBox = new St.BoxLayout({ + style_class: 'menu-search-box', + vertical: false, + }); + this.searchEntry = new St.Entry({ + name: 'menu-search-entry', + track_hover: true, + can_focus: true, + }); + this.searchBox.add(this.searchEntry, { + x_align: St.Align.START, + y_align: St.Align.MIDDLE, + expand: true, + }); this.searchEntry.set_secondary_icon(this._searchInactiveIcon); this.searchActive = false; this.searchEntryText = this.searchEntry.clutter_text; - this.searchEntryText.connect('text-changed', Lang.bind(this, this._onSearchTextChanged)); - this.searchEntryText.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress)); + this.searchEntryText.connect('text-changed', this._onSearchTextChanged.bind(this)); + this.searchEntryText.connect('key-press-event', this._onMenuKeyPress.bind(this)); this._previousSearchPattern = ""; this.right_box.add(this.searchBox, {span: 1}); this.categoriesApplicationsBox = new CategoriesApplicationsBox(); - this.right_box.add(this.categoriesApplicationsBox.actor, { expand: true, span: 1 }); + this.right_box.add(this.categoriesApplicationsBox.actor, { + expand: true, + span: 1, + }); + + this.categoriesBox = new St.BoxLayout({ + style_class: 'menu-categories-box', + vertical: true, + accessible_role: Atk.Role.LIST + }); - this.categoriesBox = new St.BoxLayout({ style_class: 'menu-categories-box', - vertical: true, - accessible_role: Atk.Role.LIST }); this.categoriesScrollBox = new St.ScrollView({ style_class: 'vfade menu-applications-scrollbox' }); this.categoriesScrollBox.add_actor(this.categoriesBox); this.categoriesScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); @@ -2749,78 +2912,77 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesApplicationsBox.actor.add(this.categoriesScrollBox); - this.applicationsBox = new St.BoxLayout({ style_class: 'menu-applications-inner-box', vertical:true }); - this.applicationsBox.add_style_class_name('menu-applications-box'); //this is to support old themes + this.applicationsBox = new St.BoxLayout({ + style_class: 'menu-applications-box', + vertical:true + }); this.applicationsScrollBox = new St.ScrollView({ style_class: 'vfade menu-applications-scrollbox'}); this.applicationsScrollBox.add_actor(this.applicationsBox); this.applicationsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.applicationsScrollBox.set_clip_to_allocation(true); let vscroll = this.applicationsScrollBox.get_vscroll_bar(); - vscroll.connect('scroll-start', - Lang.bind(this, function() { - this.menu.passEvents = true; - })); - vscroll.connect('scroll-stop', - Lang.bind(this, function() { - this.menu.passEvents = false; - })); + vscroll.connect('scroll-start', () => { + this.menu.passEvents = true; + }); + vscroll.connect('scroll-stop', () => { + this.menu.passEvents = false; + }); let vscrollCat = this.categoriesScrollBox.get_vscroll_bar(); - vscrollCat.connect('scroll-start', - Lang.bind(this, function() { - this.menu.passEvents = true; - })); - vscrollCat.connect('scroll-stop', - Lang.bind(this, function() { - this.menu.passEvents = false; - })); - this.categoriesApplicationsBox.actor.add(this.applicationsScrollBox, {span: -1, expand: true}); - - this.favoritesBox = new FavoritesBox().actor; - this.favoritesScrollBox = new St.ScrollView({ y_align: St.Align.START, style_class: 'vfade menu-favorites-scrollbox' }); - this.favoritesScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); - this.favoritesScrollBox.set_clip_to_allocation(true); - this.favoritesScrollBox.add_actor(this.favoritesBox); + vscrollCat.connect('scroll-start', () => { + this.menu.passEvents = true; + }); + vscrollCat.connect('scroll-stop', () => { + this.menu.passEvents = false; + }); - this.left_box.add(this.favoritesScrollBox, { expand: true }); + this.categoriesApplicationsBox.actor.add(this.applicationsScrollBox, {span: -1, expand: true}); - this.systemButtonsBox = new St.BoxLayout({ vertical: true, y_expand: false, y_align: Clutter.ActorAlign.END }); - this.left_box.add(this.systemButtonsBox, { expand: true }); + this.menuBottomBox = new St.BoxLayout({ + style_class: 'menu-bottom-box', + y_expand: false, + x_expand: true, + x_align: Clutter.ActorAlign.FILL + }); - this.selectedAppBox = new St.BoxLayout({ style_class: 'menu-selected-app-box', vertical: true }); - this.selectedAppTitle = new St.Label({ style_class: 'menu-selected-app-title', text: " " }); - this.selectedAppBox.add(this.selectedAppTitle); - this.selectedAppDescription = new St.Label({ style_class: 'menu-selected-app-description', text: " " }); - this.selectedAppBox.add(this.selectedAppDescription); - this.selectedAppBox._delegate = null; + this.systemButtonsBox = new St.BoxLayout({ + style_class: 'menu-system-buttons-box', + y_expand: false, + x_expand: true, + x_align: Clutter.ActorAlign.END + }); - this.right_box.add(this.selectedAppBox, {span: 1}); + this.right_box.add(this.menuBottomBox); + this.menuBottomBox.add(this.systemButtonsBox); this.appBoxIter = new VisibleChildIterator(this.applicationsBox); this.applicationsBox._vis_iter = this.appBoxIter; this.catBoxIter = new VisibleChildIterator(this.categoriesBox); this.categoriesBox._vis_iter = this.catBoxIter; - this.favBoxIter = new VisibleChildIterator(this.favoritesBox); - this.favoritesBox._vis_iter = this.favBoxIter; + this.placesBoxIter = new VisibleChildIterator(this.placesBox); + this.placesBox._vis_iter = this.placesBoxIter; + this.favBoxIter = new VisibleChildIterator(this.favoriteAppsBox); + this.favoriteAppsBox._vis_iter = this.favBoxIter; this.sysBoxIter = new VisibleChildIterator(this.systemButtonsBox); this.systemButtonsBox._vis_iter = this.sysBoxIter; - Mainloop.idle_add(Lang.bind(this, function() { + Mainloop.idle_add(() => { this._clearAllSelections(); this._hideAllAppActors(); - })); + }); this.a11y_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.a11y.applications" }); - this.a11y_settings.connect("changed::screen-magnifier-enabled", Lang.bind(this, this._updateVFade)); + this.a11y_settings.connect("changed::screen-magnifier-enabled", this._updateVFade.bind(this)); this.a11y_mag_settings = new Gio.Settings({ schema_id: "org.cinnamon.desktop.a11y.magnifier" }); - this.a11y_mag_settings.connect("changed::mag-factor", Lang.bind(this, this._updateVFade)); + this.a11y_mag_settings.connect("changed::mag-factor", this._updateVFade.bind(this)); this._updateVFade(); this.settings.bind("enable-autoscroll", "autoscroll_enabled", this._update_autoscroll); this._update_autoscroll(); - this._favboxtoggle(); + this._bottomBoxToggle(); + this._sidebarToggle(); } _updateVFade() { @@ -2829,18 +2991,18 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (mag_on) { this.applicationsScrollBox.style_class = "menu-applications-scrollbox"; this.categoriesScrollBox.style_class = "menu-applications-scrollbox"; - this.favoritesScrollBox.style_class = "menu-favorites-scrollbox"; + this.sidebarAppsScrollBox.style_class = "menu-favorites-scrollbox"; } else { this.applicationsScrollBox.style_class = "vfade menu-applications-scrollbox"; this.categoriesScrollBox.style_class = "vfade menu-applications-scrollbox"; - this.favoritesScrollBox.style_class = "vfade menu-favorites-scrollbox"; + this.sidebarAppsScrollBox.style_class = "vfade menu-favorites-scrollbox"; } } _update_autoscroll() { this.applicationsScrollBox.set_auto_scrolling(this.autoscroll_enabled); this.categoriesScrollBox.set_auto_scrolling(this.autoscroll_enabled); - this.favoritesScrollBox.set_auto_scrolling(this.autoscroll_enabled); + this.sidebarAppsScrollBox.set_auto_scrolling(this.autoscroll_enabled); } _hideAllAppActors() { @@ -2864,7 +3026,13 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { actor.style_class = "menu-category-button"; actor.show(); } - actors = this.favoritesBox.get_children(); + actors = this.placesBox.get_children(); + for (let i = 0; i < actors.length; i++){ + let actor = actors[i]; + actor.remove_style_pseudo_class("hover"); + actor.show(); + } + actors = this.favoriteAppsBox.get_children(); for (let i = 0; i < actors.length; i++){ let actor = actors[i]; actor.remove_style_pseudo_class("hover"); @@ -2889,10 +3057,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.applicationsBox.set_child_at_index(this._favoriteDocButtons[i].actor, pos++); } - for (let i = 0; i < this._placesButtons.length; i++) { - this.applicationsBox.set_child_at_index(this._placesButtons[i].actor, pos++); - } - for (let i = 0; i < this._recentButtons.length; i++) { this.applicationsBox.set_child_at_index(this._recentButtons[i].actor, pos++); } @@ -3060,8 +3224,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._select_category(); this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; this._activeContainer = null; - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); } } @@ -3131,9 +3293,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let result = this._matchNames(this._favoriteDocButtons, pattern); buttons = [...buttons, ...result]; - result = this._matchNames(this._placesButtons, pattern); - buttons = [...buttons, ...result]; - result = this._matchNames(this._recentButtons, pattern); buttons = [...buttons, ...result]; } @@ -3146,12 +3305,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._activeContainer = this.applicationsBox; this._scrollToButton(this._activeActor._delegate); this._buttonEnterEvent(this._activeActor._delegate); - } else { - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); } - SearchProviderManager.launch_all(lowerPattern, Lang.bind(this, function(provider, results) { + SearchProviderManager.launch_all(lowerPattern, (provider, results) => { try { for (var i in results) { if (results[i].type != 'software') @@ -3172,7 +3328,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } catch(e) { global.log(e); } - })); + }); return false; } diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index 509ba08afa..572fbd0f2a 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -27,7 +27,8 @@ "title" : "Layout and content", "keys" : ["show-category-icons", "category-icon-size", "show-application-icons", "application-icon-size", - "favbox-show", "fav-icon-size", "show-places", "show-recents", "menu-editor-button", "reset-menu-size-button"] + "sidebar-show", "sidebar-icon-size", "bottombox-show", "show-recents", + "show-description", "menu-editor-button", "reset-menu-size-button"] }, "menu-behave" : { "type" : "section", @@ -79,7 +80,7 @@ }, "category-icon-size" : { "type": "spinbutton", - "default" : 22, + "default" : 32, "min" : 16, "max" : 48, "step" : 1, @@ -95,7 +96,7 @@ }, "application-icon-size" : { "type": "spinbutton", - "default" : 22, + "default" : 32, "min" : 16, "max" : 48, "step" : 1, @@ -103,21 +104,27 @@ "description" : "Applications icon size", "dependency" : "show-application-icons" }, - "favbox-show" : { + "sidebar-show" : { "type" : "switch", "default" : true, - "description" : "Show favorites and session buttons", + "description" : "Show the sidebar", "tooltip" : "Choose whether or not to show the left pane of the menu." }, - "fav-icon-size" : { + "sidebar-icon-size" : { "type": "spinbutton", - "default" : 32, + "default" : 24, "min" : 16, - "max" : 64, + "max" : 48, "step" : 1, "units" : "px", - "description" : "Favorites icon size", - "dependency" : "favbox-show" + "description" : "Sidebar icon size", + "dependency" : "sidebar-show" + }, + "bottombox-show" : { + "type" : "switch", + "default" : true, + "description" : "Show the bottom bar", + "tooltip" : "Choose whether or not to show the bottom bar of the menu." }, "show-favorites" : { "type" : "switch", @@ -125,18 +132,18 @@ "description": "Show favorites", "tooltip": "Choose whether or not to show favorite files in the menu." }, - "show-places" : { - "type" : "switch", - "default" : true, - "description": "Show bookmarks and places", - "tooltip": "Choose whether or not to show bookmarks and places in the menu." - }, "show-recents" : { "type" : "switch", "default" : true, "description": "Show recents", "tooltip": "Choose whether or not to show recents in the menu." }, +"show-description" : { + "type" : "switch", + "default" : true, + "description": "Show application descriptions", + "tooltip": "Choose whether or not to show application descriptions in the menu." + }, "category-hover" : { "type" : "switch", "default" : true, @@ -196,7 +203,7 @@ }, "popup-height" : { "type" : "generic", - "default" : 515 + "default" : 560 }, "reset-menu-size-button" : { "type" : "button", From d0872de853e35498ea62ee0fdadc39b11c694bc8 Mon Sep 17 00:00:00 2001 From: JosephMcc Date: Mon, 3 Mar 2025 06:14:29 -0800 Subject: [PATCH 02/16] menu: Increase the POLL_INTERVAL This helps prevent accidentally triggering another category when moving the mouse from the categories to the application column. --- files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 3921614e55..b50c7e4388 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -2353,7 +2353,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { */ static DEBUG_VMASK = false; - static POLL_INTERVAL = 20; + static POLL_INTERVAL = 30; static MIN_MOVEMENT = 2; // Movement smaller than this disables the mask. _getNewVectorInfo() { From 92800b21665247989c276cc5fbcfb72631462025 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Wed, 24 Sep 2025 16:29:46 +0100 Subject: [PATCH 03/16] menu: Shorten path info for recents --- .../usr/share/cinnamon/applets/menu@cinnamon.org/applet.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index b50c7e4388..8414d1300d 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -756,12 +756,12 @@ class UserfileContextMenuItem extends PopupMenu.PopupBaseMenuItem { class RecentButton extends SimpleMenuItem { constructor(applet, recent) { - let fileIndex = recent.uriDecoded.indexOf("file:///"); - let selectedAppUri = fileIndex === -1 ? "" : recent.uriDecoded.substr(fileIndex + 7); + let path = recent.uriDecoded.replace("file://", ""); + path = path.replace(GLib.get_home_dir(), "~").replace("/" + recent.name, ""); super(applet, { name: recent.name, - description: selectedAppUri, + description: path, type: 'recent', styleClass: 'menu-application-button', withMenu: true, From d1ea12a279cf21f4132f6b14c7f67d576bea924f Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Wed, 24 Sep 2025 17:50:56 +0100 Subject: [PATCH 04/16] menu: Simplify path buttons Use a unique class for recent files and favorite files. --- .../applets/menu@cinnamon.org/applet.js | 183 +++--------------- 1 file changed, 30 insertions(+), 153 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 8414d1300d..4bf359da90 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -83,6 +83,13 @@ function calc_angle(x, y) { return r; } +function shorten_path(path, filename) { + let str = path.replace("file://", ""); + str = str.replace(GLib.get_home_dir(), "~"); + str = str.replace("/" + filename, ""); + return str; +} + class VisibleChildIterator { constructor(container) { this.container = container; @@ -138,7 +145,8 @@ class VisibleChildIterator { * no-recent "No recent documents" button * none Default type * place PlaceButton - * recent RecentsButton + * favorite PathButton + * recent PathButton * recent-clear "Clear recent documents" button * search-provider SearchProviderResultButton * system SystemButton @@ -765,9 +773,7 @@ class RecentButton extends SimpleMenuItem { type: 'recent', styleClass: 'menu-application-button', withMenu: true, - mimeType: recent.mimeType, uri: recent.uri, - uriDecoded: recent.uriDecoded, }); this.icon = recent.createIcon(applet.applicationIconSize); @@ -799,107 +805,37 @@ class RecentButton extends SimpleMenuItem { source.notify(notification); } } - - hasLocalPath(file) { - return file.is_native() || file.get_path() != null; - } - - populateMenu(menu) { - let menuItem; - menuItem = new PopupMenu.PopupMenuItem(_("Open with"), { reactive: false }); - menuItem.actor.style = "font-weight: bold"; - menu.addMenuItem(menuItem); - - let file = Gio.File.new_for_uri(this.uri); - - let default_info = Gio.AppInfo.get_default_for_type(this.mimeType, !this.hasLocalPath(file)); - - let infoLaunchFunc = (info, file) => { - info.launch([file], null); - this.applet.toggleContextMenu(this); - this.applet.menu.close(); - }; - - if (default_info) { - menuItem = new UserfileContextMenuItem(this, - default_info.get_display_name(), - false, - [default_info, file], - infoLaunchFunc); - menu.addMenuItem(menuItem); - } - - let infos = Gio.AppInfo.get_all_for_type(this.mimeType); - - for (let i = 0; i < infos.length; i++) { - let info = infos[i]; - - file = Gio.File.new_for_uri(this.uri); - - if (!this.hasLocalPath(file) && !info.supports_uris()) - continue; - - if (info.equal(default_info)) - continue; - - menuItem = new UserfileContextMenuItem(this, - info.get_display_name(), - false, - [info, file], - infoLaunchFunc); - menu.addMenuItem(menuItem); - } - - if (GLib.find_program_in_path ("nemo-open-with") != null) { - menuItem = new UserfileContextMenuItem(this, - _("Other application..."), - false, - [], - () => { - Util.spawnCommandLine("nemo-open-with " + this.uri); - this.applet.toggleContextMenu(this); - this.applet.menu.close(); - }); - menu.addMenuItem(menuItem); - } - } } -class FavoriteButton extends SimpleMenuItem { - constructor(applet, favoriteInfo) { +class PathButton extends SimpleMenuItem { + constructor(applet, type, name, uri, icon) { super(applet, { - name: favoriteInfo.display_name, - description: decodeURIComponent(favoriteInfo.uri), - type: 'favorite', + name: name, + description: shorten_path(uri, name), + type: type, styleClass: 'menu-application-button', - withMenu: true, - mimeType: favoriteInfo.cached_mimetype, - uri: favoriteInfo.uri, - }); - - this.favoriteInfo = favoriteInfo; - - let gicon = Gio.content_type_get_icon(favoriteInfo.cached_mimetype); - this.icon = new St.Icon({ - gicon: gicon, - icon_size: applet.applicationIconSize, + withMenu: false, + uri: uri, }); + this.icon = icon; this.addActor(this.icon); - if (!applet.showApplicationIcons) this.icon.visible = false; - this.addLabel(this.name, 'menu-application-button-label'); + this.addLabel(name, 'menu-application-button-label'); + + if (applet.showDescription) + this.addDescription(this.description, 'menu-application-button-description'); this.searchStrings = [ - AppUtils.decomp_string(favoriteInfo.display_name).replace(/\s/g, '') + AppUtils.decomp_string(name).replace(/\s/g, '') ]; } activate() { try { - XApp.Favorites.get_default().launch(this.uri, 0); + Gio.app_info_launch_default_for_uri(this.uri, global.create_app_launch_context()); this.applet.menu.close(); } catch (e) { let source = new MessageTray.SystemNotificationSource(); @@ -912,70 +848,6 @@ class FavoriteButton extends SimpleMenuItem { source.notify(notification); } } - - hasLocalPath(file) { - return file.is_native() || file.get_path() != null; - } - - populateMenu(menu) { - let menuItem; - menuItem = new PopupMenu.PopupMenuItem(_("Open with"), { reactive: false }); - menuItem.actor.style = "font-weight: bold"; - menu.addMenuItem(menuItem); - - let file = Gio.File.new_for_uri(this.uri); - - let default_info = Gio.AppInfo.get_default_for_type(this.mimeType, !this.hasLocalPath(file)); - - let infoLaunchFunc = (info, file) => { - info.launch([file], null); - this.applet.toggleContextMenu(this); - this.applet.menu.close(); - }; - - if (default_info) { - menuItem = new UserfileContextMenuItem(this, - default_info.get_display_name(), - false, - [default_info, file], - infoLaunchFunc); - menu.addMenuItem(menuItem); - } - - let infos = Gio.AppInfo.get_all_for_type(this.mimeType); - - for (let i = 0; i < infos.length; i++) { - let info = infos[i]; - - file = Gio.File.new_for_uri(this.uri); - - if (!this.hasLocalPath(file) && !info.supports_uris()) - continue; - - if (info.equal(default_info)) - continue; - - menuItem = new UserfileContextMenuItem(this, - info.get_display_name(), - false, - [info, file], - infoLaunchFunc); - menu.addMenuItem(menuItem); - } - - if (GLib.find_program_in_path ("nemo-open-with") != null) { - menuItem = new UserfileContextMenuItem(this, - _("Other application..."), - false, - [], - () => { - Util.spawnCommandLine("nemo-open-with " + this.uri); - this.applet.toggleContextMenu(this); - this.applet.menu.close(); - }); - menu.addMenuItem(menuItem); - } - } } class CategoryButton extends SimpleMenuItem { @@ -2567,7 +2439,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (recents.length > 0) { this.noRecentDocuments = false; recents.forEach( info => { - let button = new RecentButton(this, info); + let icon = info.createIcon(this.applicationIconSize); + let button = new PathButton(this, 'recent', info.name, info.uri, icon); this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.visible = this.menu.isOpen && this.lastSelectedCategory === "recent"; @@ -2634,7 +2507,11 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } favorite_infos.forEach( info => { - let button = new FavoriteButton(this, info); + let icon = new St.Icon({ + gicon: Gio.content_type_get_icon(info.cached_mimetype), + icon_size: this.applicationIconSize + }); + let button = new PathButton(this, 'favorite', info.display_name, info.uri, icon); this._favoriteDocButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.visible = this.menu.isOpen && this.lastSelectedCategory === "favorite"; From f9c91dd4a6d45178e71f5084934762e27d4730bb Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Fri, 26 Sep 2025 11:06:05 +0100 Subject: [PATCH 05/16] menu: Don't show devices Removable ones are already in: - nemo (which pops up when plugged) - removable-drives applet Permanent ones don't need quick-access, are already in nemo sidebar and can be bookmarked. --- .../usr/share/cinnamon/applets/menu@cinnamon.org/applet.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 4bf359da90..5fb4923b3c 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -2398,9 +2398,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { //Load places again this._placesButtons = []; - let places = Main.placesManager.getAllPlaces(); - for (let i = 0; i < places.length; ++i ) { - let button = new PlaceButton(this, places[i]); + let places = [...Main.placesManager.getDefaultPlaces(), ...Main.placesManager.getBookmarks()] + for (let place of places) { + let button = new PlaceButton(this, place); this._placesButtons.push(button); this.placesBox.add(button.actor, { y_align: St.Align.END, From a17f15b1599d0c27f484c6a0dcd970ec3045caf4 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Fri, 26 Sep 2025 11:29:49 +0100 Subject: [PATCH 06/16] menu: Remove unnecessary code - Unused code - search-filesystem Very niche and didn't work properly. --- .../applets/menu@cinnamon.org/applet.js | 168 +----------------- .../menu@cinnamon.org/settings-schema.json | 8 +- 2 files changed, 6 insertions(+), 170 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 5fb4923b3c..f2988fed42 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -117,23 +117,12 @@ class VisibleChildIterator { return this.array[this.array.length - 1]; } - getVisibleIndex(curChild) { - return this.array.indexOf(curChild); - } - getVisibleItem(index) { let len = this.array.length; index = ((index % len) + len) % len; return this.array[index]; } - getNumVisibleChildren() { - return this.array.length; - } - - getAbsoluteIndexOfChild(child) { - return this.container.get_children().indexOf(child); - } } /** @@ -272,16 +261,6 @@ class SimpleMenuItem { this.actor.add_actor(this.icon); } - /** - * Removes the icon previously added with addIcon() - */ - removeIcon() { - if (!this.icon) - return; - this.icon.destroy(); - this.icon = null; - } - /** * Adds an StLabel as the next child, accessible as `this.label`. * @@ -303,16 +282,6 @@ class SimpleMenuItem { this.labelContainer.add_actor(this.label); } - /** - * Removes the label previously added with addLabel() - */ - removeLabel() { - if (!this.label) - return; - this.label.destroy(); - this.label = null; - } - addDescription(label='', styleClass=null) { if (this.descriptionLabel) return; @@ -341,15 +310,6 @@ class SimpleMenuItem { this.actor.add_actor(child); } - /** - * Removes a ClutterActor. - * - * @param {ClutterActor} child - */ - removeActor(child) { - this.actor.remove_actor(child); - } - destroy(actorDestroySignal=false) { this._signals.disconnectAllSignals(); @@ -742,26 +702,6 @@ class PlaceButton extends SimpleMenuItem { } } -class UserfileContextMenuItem extends PopupMenu.PopupBaseMenuItem { - constructor(button, label, is_default, cbParams, callback) { - super({focusOnHover: false}); - - this._button = button; - this._cbParams = cbParams; - this._callback = callback; - this.label = new St.Label({ text: label }); - this.addActor(this.label); - - if (is_default) - this.label.style = "font-weight: bold;"; - } - - activate (event) { - this._callback(...this._cbParams); - return false; - } -} - class RecentButton extends SimpleMenuItem { constructor(applet, recent) { let path = recent.uriDecoded.replace("file://", ""); @@ -1232,10 +1172,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.RecentManager.connect('changed', () => this.queueRefresh(RefreshFlags.RECENT)); this.privacy_settings.connect("changed::" + REMEMBER_RECENT_KEY, () => this.queueRefresh(RefreshFlags.RECENT)); XApp.Favorites.get_default().connect("changed", () => this.queueRefresh(RefreshFlags.FAV_DOC)); - this._fileFolderAccessActive = false; this._pathCompleter = new Gio.FilenameCompleter(); this._pathCompleter.set_dirs_only(false); - this.settings.bind("search-filesystem", "searchFilesystem"); this.contextMenu = null; this.lastSelectedCategory = null; this.settings.bind("force-show-panel", "forceShowPanel"); @@ -2063,50 +2001,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { favPos = selectedItemIndex - 1; appFavorites.moveFavoriteToPos(id, favPos); item_actor = this.favoriteAppsBox.get_child_at_index(favPos); - } else if (this.searchFilesystem && (this._fileFolderAccessActive || symbol === Clutter.KEY_slash)) { - if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) { - if (this._run(this.searchEntry.get_text())) { - this.menu.close(); - } - return true; - } - if (symbol === Clutter.KEY_Escape) { - this.searchEntry.set_text(''); - this._fileFolderAccessActive = false; - } - if (symbol === Clutter.KEY_slash) { - // Need preload data before get completion. GFilenameCompleter load content of parent directory. - // Parent directory for /usr/include/ is /usr/. So need to add fake name('a'). - let text = this.searchEntry.get_text().concat('/a'); - let prefix; - if (!text.includes(' ')) - prefix = text; - else - prefix = text.substr(text.lastIndexOf(' ') + 1); - this._getCompletion(prefix); - - return false; - } - if (symbol === Clutter.KEY_Tab) { - let text = actor.get_text(); - let prefix; - if (!text.includes(' ')) - prefix = text; - else - prefix = text.substr(text.lastIndexOf(' ') + 1); - let postfix = this._getCompletion(prefix); - if (postfix != null && postfix.length > 0) { - actor.insert_text(postfix, -1); - actor.set_cursor_position(text.length + postfix.length); - if (postfix[postfix.length - 1] == '/') - this._getCompletion(text + postfix + 'a'); - } - return true; - } - if (symbol === Clutter.KEY_ISO_Left_Tab) { - return true; - } - return false; } else if (symbol === Clutter.KEY_Tab || symbol === Clutter.KEY_ISO_Left_Tab) { return true; } else { @@ -2726,7 +2620,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._activeActor = null; this.actor_motion_id = 0; this.vector_update_loop = 0; - this.foobar = 0; let section = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(section); @@ -3075,7 +2968,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._previousSearchPattern = searchString; this.searchActive = searchActive; - this._fileFolderAccessActive = searchActive && this.searchFilesystem; this._clearAllSelections(); if (searchActive) { @@ -3161,18 +3053,13 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { var acResultButtons = []; // search box autocompletion results var buttons = [] - if (this.searchFilesystem && ["~", "/"].includes(rawPattern[0])) { - // Don't use the pattern here, as filesystem is case sensitive - acResultButtons = this._getCompletions(rawPattern); - } else { - buttons = this._listApplications(pattern); + buttons = this._listApplications(pattern); - let result = this._matchNames(this._favoriteDocButtons, pattern); - buttons = [...buttons, ...result]; + let result = this._matchNames(this._favoriteDocButtons, pattern); + buttons = [...buttons, ...result]; - result = this._matchNames(this._recentButtons, pattern); - buttons = [...buttons, ...result]; - } + result = this._matchNames(this._recentButtons, pattern); + buttons = [...buttons, ...result]; this._displayButtons(null, buttons, acResultButtons); @@ -3209,51 +3096,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { return false; } - - _getCompletion (text) { - if (!text.includes('/') || text.endsWith('/')) - return ''; - return this._pathCompleter.get_completion_suffix(text); - } - - _getCompletions (text) { - if (!text.includes('/')) - return []; - return this._pathCompleter.get_completions(text); - } - - _run (input) { - this._commandError = false; - if (input) { - let path = null; - if (input.startsWith('/')) { - path = input; - } else { - if (input.startsWith('~')) - input = input.slice(1); - path = GLib.get_home_dir() + '/' + input; - } - - if (GLib.file_test(path, GLib.FileTest.EXISTS)) { - let file = Gio.file_new_for_path(path); - try { - Gio.app_info_launch_default_for_uri(file.get_uri(), - global.create_app_launch_context()); - } catch (e) { - // The exception from gjs contains an error string like: - // Error invoking Gio.app_info_launch_default_for_uri: No application - // is registered as handling this file - // We are only interested in the part after the first colon. - //let message = e.message.replace(/[^:]*: *(.+)/, '$1'); - return false; - } - } else { - return false; - } - } - - return true; - } }; function main(metadata, orientation, panel_height, instance_id) { diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index 572fbd0f2a..90e53d1bf9 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -33,7 +33,7 @@ "menu-behave" : { "type" : "section", "title" : "Behavior", - "keys" : ["category-hover", "enable-autoscroll", "search-filesystem"] + "keys" : ["category-hover", "enable-autoscroll"] } }, "overlay-key" : { @@ -156,12 +156,6 @@ "description": "Enable autoscrolling in application list", "tooltip": "Choose whether or not to enable smooth autoscrolling in the application list." }, -"search-filesystem" : { - "type" : "switch", - "default" : false, - "description": "Enable filesystem path entry in search box", - "tooltip": "Allows path entry in the menu search box." - }, "force-show-panel" : { "type" : "switch", "default" : true, From 9fe15a3b810f4601fb08d1bfa6f931093a06898f Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Fri, 26 Sep 2025 13:58:43 +0100 Subject: [PATCH 07/16] menu: Fix apps using icon-names not on the theme reproducible with picom --- .../cinnamon/applets/menu@cinnamon.org/applet.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index f2988fed42..ab4810249b 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -574,8 +574,20 @@ class ApplicationButton extends GenericApplicationButton { constructor(applet, app) { super(applet, app, 'app', true, 'menu-application-button'); this.category = []; - this.icon = this.app.create_icon_texture(applet.applicationIconSize); + let gicon = this.icon.get_gicon(); + if (gicon?.get_names) { + let iconNames = gicon.get_names(); + let iconTheme = Gtk.IconTheme.get_default(); + let hasAnyIcon = gicon.get_names().some(name => iconTheme.has_icon(name)); + if (!hasAnyIcon) { + this.icon = new St.Icon({ + icon_name: 'application-x-executable', + icon_size: applet.applicationIconSize, + icon_type: St.IconType.FULLCOLOR + }); + } + } this.addActor(this.icon); if (!applet.showApplicationIcons) this.icon.visible = false; From 842603425d8716446b4d108ab264c23b739b5887 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Sat, 27 Sep 2025 19:29:40 +0100 Subject: [PATCH 08/16] menu: Modernize JS code - Use ES2105 for loops (similar to python) - forEach/filter/map for one-liners --- .../applets/menu@cinnamon.org/applet.js | 122 +++++++----------- 1 file changed, 47 insertions(+), 75 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index ab4810249b..b6885b1de0 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -486,14 +486,11 @@ class GenericApplicationButton extends SimpleMenuItem { menu.addMenuItem(menuItem); } - let actions = appinfo.list_actions(); - if (actions) { - for (let i = 0; i < actions.length; i++) { - let icon = Util.getDesktopActionIcon(actions[i]); - let label = appinfo.get_action_name(actions[i]); - menuItem = new ApplicationContextMenuItem(this, label, "action_" + actions[i], icon); - menu.addMenuItem(menuItem); - } + for (action of appinfo.list_actions()) { + let icon = Util.getDesktopActionIcon(action); + let label = appinfo.get_action_name(action); + menuItem = new ApplicationContextMenuItem(this, label, "action_" + action, icon); + menu.addMenuItem(menuItem); } } } @@ -2291,9 +2288,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _setCategoryButtonsReactive(active) { - for (let i = 0; i < this._categoryButtons.length; i++) { - this._categoryButtons[i].actor.reactive = active; - this._categoryButtons[i].actor.queue_redraw(); + for (let button of this._categoryButtons) { + button.actor.reactive = active; + button.actor.queue_redraw(); } } @@ -2316,19 +2313,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _refreshRecent () { - for (let i = 0; i < this._recentButtons.length; i++) { - this._recentButtons[i].destroy(); - } - + this._recentButtons.forEach(button => button.destroy()); this._recentButtons = []; - for (let i = 0; i < this._categoryButtons.length; i++) { - if (this._categoryButtons[i].categoryId === 'recent') { - this._categoryButtons[i].destroy(); - this._categoryButtons.splice(i, 1); - this.recentButton = null; - break; - } + const index = this._categoryButtons.findIndex(button => button.categoryId === 'recent'); + if (index !== -1) { + this._categoryButtons[index].destroy(); + this._categoryButtons.splice(index, 1); + this.recentButton = null; } if (!this.showRecents || !this.privacy_settings.get_boolean(REMEMBER_RECENT_KEY)) { @@ -2385,19 +2377,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _refreshFavDocs() { - for (let i = 0; i < this._favoriteDocButtons.length; i++) { - this._favoriteDocButtons[i].destroy(); - } - + this._favoriteDocButtons.forEach(button => button.destroy()); this._favoriteDocButtons = []; - for (let i = 0; i < this._categoryButtons.length; i++) { - if (this._categoryButtons[i].categoryId === 'favorite') { - this._categoryButtons[i].destroy(); - this._categoryButtons.splice(i, 1); - this.favoriteDocsButton = null; - break; - } + const index = this._categoryButtons.findIndex(button => button.categoryId === 'favorite'); + if (index !== -1) { + this._categoryButtons[index].destroy(); + this._categoryButtons.splice(index, 1); + this.favoriteDocsButton = null; } let favorite_infos = XApp.Favorites.get_default().get_favorites(null); @@ -2492,8 +2479,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._favoriteAppButtons = []; let launchers = global.settings.get_strv('favorite-apps'); - for ( let i = 0; i < launchers.length; ++i ) { - let app = appsys.lookup_app(launchers[i]); + for (let launcher of launchers) { + let app = appsys.lookup_app(launcher); if (app) { let button = new FavoritesButton(this, app); this._favoriteAppButtons[app] = button; @@ -2788,44 +2775,32 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _hideAllAppActors() { - let actors = this.applicationsBox.get_children(); - for (let i = 0; i < actors.length; i++) { - let actor = actors[i]; + this.applicationsBox.get_children().forEach(actor => { actor.hide(); - } + }); } _clearAllSelections() { - let actors = this.applicationsBox.get_children(); - for (let i = 0; i < actors.length; i++) { - let actor = actors[i]; + this.applicationsBox.get_children().forEach(actor => { actor.style_class = "menu-application-button"; - } - actors = this.categoriesBox.get_children(); - for (let i = 0; i < actors.length; i++){ - let actor = actors[i]; + }); + this.categoriesBox.get_children().forEach(actor => { actor.remove_style_pseudo_class("hover") actor.style_class = "menu-category-button"; actor.show(); - } - actors = this.placesBox.get_children(); - for (let i = 0; i < actors.length; i++){ - let actor = actors[i]; + }); + this.placesBox.get_children().forEach(actor => { actor.remove_style_pseudo_class("hover"); actor.show(); - } - actors = this.favoriteAppsBox.get_children(); - for (let i = 0; i < actors.length; i++){ - let actor = actors[i]; + }); + this.favoriteAppsBox.get_children().forEach(actor => { actor.remove_style_pseudo_class("hover"); actor.show(); - } - actors = this.systemButtonsBox.get_children(); - for (let i = 0; i < actors.length; i++){ - let actor = actors[i]; + }); + this.systemButtonsBox.get_children().forEach(actor => { actor.remove_style_pseudo_class("hover"); actor.show(); - } + }); } _resetSortOrder() { @@ -3012,16 +2987,16 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let ret = []; let regexpPattern = new RegExp(Util.escapeRegExp(pattern)); - for (let i = 0; i < buttons.length; i++) { - if (buttons[i].type == "recent-clear" || buttons[i].type == "no-recent") { + for (let button of buttons) { + if (button.type == "recent-clear" || button.type == "no-recent") { continue; } - let res = buttons[i].searchStrings[0].match(regexpPattern); + let res = button.searchStrings[0].match(regexpPattern); if (res) { - buttons[i].matchIndex = res.index; - ret.push(buttons[i]); + button.matchIndex = res.index; + ret.push(button); } else { - buttons[i].matchIndex = NO_MATCH; + button.matchIndex = NO_MATCH; } } @@ -3035,13 +3010,11 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let apps = []; let regexpPattern = new RegExp(Util.escapeRegExp(pattern)); - for (let i in this._applicationsButtons) { - let button = this._applicationsButtons[i]; - - for (let j = 0; j < button.searchStrings.length; j++) { - let res = button.searchStrings[j].match(regexpPattern); + for (let button of this._applicationsButtons) { + for (let i = 0; i < button.searchStrings.length; i++) { + let res = button.searchStrings[i].match(regexpPattern); if (res) { - button.matchIndex = res.index + MATCH_ADDERS[j]; + button.matchIndex = res.index + MATCH_ADDERS[i]; apps.push(button); break; } else { @@ -3085,10 +3058,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { SearchProviderManager.launch_all(lowerPattern, (provider, results) => { try { - for (var i in results) { - if (results[i].type != 'software') - { - let button = new SearchProviderResultButton(this, provider, results[i]); + for (let result of results) { + if (result.type != 'software') { + let button = new SearchProviderResultButton(this, provider, result); this._searchProviderButtons.push(button); this.applicationsBox.add_actor(button.actor); if (this._activeActor === null) { From 7a6b27507ac7dd5d651a8b224477d80a1f32aad2 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Sat, 27 Sep 2025 19:52:25 +0100 Subject: [PATCH 09/16] menu: Simplify applet Remove show-applications-icons. Simplify refresh. When refreshing after a change in the prefs, don't be selective, refresh everything. We're not looking for performance here. This actually fixed a bug since favdocs catbutton wasn't refreshed earlier on. Simplify navigation. This was overly complicated. Simplify it with .left/.right/.up/.down attributes. --- .../applets/menu@cinnamon.org/applet.js | 572 ++++++------------ .../menu@cinnamon.org/settings-schema.json | 19 +- 2 files changed, 183 insertions(+), 408 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index b6885b1de0..a077b4b0da 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -61,20 +61,6 @@ const MATCH_ADDERS = [ 3000 // id ]; -/* VisibleChildIterator takes a container (boxlayout, etc.) - * and creates an array of its visible children and their index - * positions. We can then work through that list without - * mucking about with positions and math, just give a - * child, and it'll give you the next or previous, or first or - * last child in the list. - * - * We could have this object regenerate off a signal - * every time the visibles have changed in our applicationBox, - * but we really only need it when we start keyboard - * navigating, so increase speed, we reload only when we - * want to use it. - */ - function calc_angle(x, y) { if (x === 0) { x = .001 } if (y === 0) { y = .001 } @@ -90,6 +76,20 @@ function shorten_path(path, filename) { return str; } + +/* VisibleChildIterator takes a container (boxlayout, etc.) + * and creates an array of its visible children and their index + * positions. We can then work through that list without + * mucking about with positions and math, just give a + * child, and it'll give you the next or previous, or first or + * last child in the list. + * + * We could have this object regenerate off a signal + * every time the visibles have changed in our applicationBox, + * but we really only need it when we start keyboard + * navigating, so increase speed, we reload only when we + * want to use it. + */ class VisibleChildIterator { constructor(container) { this.container = container; @@ -101,19 +101,19 @@ class VisibleChildIterator { .filter(x => !(x._delegate instanceof PopupMenu.PopupSeparatorMenuItem)); } - getNextVisible(curChild) { + next(curChild) { return this.getVisibleItem(this.array.indexOf(curChild) + 1); } - getPrevVisible(curChild) { + prev(curChild) { return this.getVisibleItem(this.array.indexOf(curChild) - 1); } - getFirstVisible() { + first() { return this.array[0]; } - getLastVisible() { + last() { return this.array[this.array.length - 1]; } @@ -486,7 +486,7 @@ class GenericApplicationButton extends SimpleMenuItem { menu.addMenuItem(menuItem); } - for (action of appinfo.list_actions()) { + for (const action of appinfo.list_actions()) { let icon = Util.getDesktopActionIcon(action); let label = appinfo.get_action_name(action); menuItem = new ApplicationContextMenuItem(this, label, "action_" + action, icon); @@ -522,30 +522,28 @@ class TransientButton extends SimpleMenuItem { this.file = Gio.file_new_for_path(this.pathOrCommand); - if (applet.showApplicationIcons) { - try { - this.handler = this.file.query_default_handler(null); - let contentType = Gio.content_type_guess(this.pathOrCommand, null); - let themedIcon = Gio.content_type_get_icon(contentType[0]); - this.icon = new St.Icon({ - gicon: themedIcon, - icon_size: applet.applicationIconSize, - icon_type: St.IconType.FULLCOLOR, - }); - } catch (e) { - this.handler = null; - let iconName = this.isPath ? 'folder' : 'unknown'; - this.icon = new St.Icon({ - icon_name: iconName, - icon_size: applet.applicationIconSize, - icon_type: St.IconType.FULLCOLOR, - }); - // @todo Would be nice to indicate we don't have a handler for this file. - } - - this.addActor(this.icon); + try { + this.handler = this.file.query_default_handler(null); + let contentType = Gio.content_type_guess(this.pathOrCommand, null); + let themedIcon = Gio.content_type_get_icon(contentType[0]); + this.icon = new St.Icon({ + gicon: themedIcon, + icon_size: applet.applicationIconSize, + icon_type: St.IconType.FULLCOLOR, + }); + } catch (e) { + this.handler = null; + let iconName = this.isPath ? 'folder' : 'unknown'; + this.icon = new St.Icon({ + icon_name: iconName, + icon_size: applet.applicationIconSize, + icon_type: St.IconType.FULLCOLOR, + }); + // @todo Would be nice to indicate we don't have a handler for this file. } + this.addActor(this.icon); + this.addLabel(this.description, 'menu-application-button-label'); this.isDraggableApp = false; @@ -586,8 +584,6 @@ class ApplicationButton extends GenericApplicationButton { } } this.addActor(this.icon); - if (!applet.showApplicationIcons) - this.icon.visible = false; this.addLabel(this.name, 'menu-application-button-label'); if (applet.showDescription) @@ -640,22 +636,20 @@ class SearchProviderResultButton extends SimpleMenuItem { result: result, }); - if (applet.showApplicationIcons) { - if (result.icon) { - this.icon = result.icon; - } else if (result.icon_app) { - this.icon = result.icon_app.create_icon_texture(applet.applicationIconSize); - } else if (result.icon_filename) { - this.icon = new St.Icon({ - gicon: new Gio.FileIcon({ file: Gio.file_new_for_path(result.icon_filename) }), - icon_size: applet.applicationIconSize, - }); - } - - if (this.icon) - this.addActor(this.icon); + if (result.icon) { + this.icon = result.icon; + } else if (result.icon_app) { + this.icon = result.icon_app.create_icon_texture(applet.applicationIconSize); + } else if (result.icon_filename) { + this.icon = new St.Icon({ + gicon: new Gio.FileIcon({ file: Gio.file_new_for_path(result.icon_filename) }), + icon_size: applet.applicationIconSize, + }); } + if (this.icon) + this.addActor(this.icon); + this.addLabel(result.label, 'menu-application-button-label'); } @@ -699,9 +693,6 @@ class PlaceButton extends SimpleMenuItem { else this.addIcon(applet.applicationIconSize, 'folder'); - if (!applet.showApplicationIcons) - this.icon.visible = false; - this.addLabel(this.name, 'menu-application-button-label'); } @@ -727,8 +718,6 @@ class RecentButton extends SimpleMenuItem { this.icon = recent.createIcon(applet.applicationIconSize); this.addActor(this.icon); - if (!applet.showApplicationIcons) - this.icon.visible = false; this.addLabel(this.name, 'menu-application-button-label'); if (applet.showDescription) @@ -769,8 +758,6 @@ class PathButton extends SimpleMenuItem { this.icon = icon; this.addActor(this.icon); - if (!applet.showApplicationIcons) - this.icon.visible = false; this.addLabel(name, 'menu-application-button-label'); @@ -826,7 +813,7 @@ class CategoryButton extends SimpleMenuItem { if(this.applet.searchActive || this.categoryId === this.applet.lastSelectedCategory) return; this.applet._select_category(this.categoryId); - this.applet._previousSelectedAppActor = null; + this.applet.hoveredApp = null; this.applet.categoriesBox.get_children().forEach(child => child.set_style_class_name("menu-category-button")); this.actor.style_class = "menu-category-button-selected"; @@ -1123,15 +1110,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("menu-icon-size", "menuIconSize", this._updateIconAndLabel); this.settings.bind("menu-label", "menuLabel", this._updateIconAndLabel); this.settings.bind("overlay-key", "overlayKey", this._updateKeybinding); - this.settings.bind("show-category-icons", "showCategoryIcons", () => this._updateShowIcons(this.categoriesBox, this.showCategoryIcons)); - this.settings.bind("category-icon-size", "categoryIconSize", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); - this.settings.bind("show-application-icons", "showApplicationIcons", () => this._updateShowIcons(this.applicationsBox, this.showApplicationIcons)); + this.settings.bind("show-category-icons", "showCategoryIcons", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("category-icon-size", "categoryIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("category-hover", "categoryHover", this._updateCategoryHover); - this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); - this.settings.bind("show-description", "showDescription", () => this.queueRefresh(RefreshFlags.RECENT | RefreshFlags.APP)); - this.settings.bind("sidebar-show", "sidebarShow", this._sidebarToggle); - this.settings.bind("sidebar-icon-size", "sidebarIconSize", () => this.queueRefresh(RefreshFlags.FAV_APP | RefreshFlags.PLACE)); - this.settings.bind("bottombox-show", "bottomBoxShow", this._bottomBoxToggle); + this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-description", "showDescription", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-sidebar", "showSidebar", this._sidebarToggle); + this.settings.bind("sidebar-icon-size", "sidebarIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-bottombox", "showBottomBox", this._bottomBoxToggle); this.settings.bind("enable-animation", "enableAnimation", null); this.settings.bind("popup-width", "popup_width"); this.settings.bind("popup-height", "popup_height"); @@ -1162,8 +1148,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._favoriteDocButtons = []; this._categoryButtons = []; this._searchProviderButtons = []; - this._previousSelectedAppActor = null; - this._previousCategoryHoverActor = null; + this.hoveredApp = null; + this.hoveredCategory = null; this._activeContainer = null; this._activeActor = null; this._knownApps = new Set(); // Used to keep track of apps that are already installed, so we can highlight newly installed ones @@ -1202,16 +1188,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.set_show_label_in_vertical_panels(false); } - _updateShowIcons(container, show) { - container.get_children().forEach( c => { - let b = c._delegate; - if (!(b instanceof SimpleMenuItem)) - return; - if (b.icon) - b.icon.visible = show; - }); - } - _onBoxResized(width, height) { width = (width / global.ui_scale).clamp(POPUP_MIN_WIDTH, POPUP_MAX_WIDTH); height = (height / global.ui_scale).clamp(POPUP_MIN_HEIGHT, POPUP_MAX_HEIGHT); @@ -1415,8 +1391,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.resetSearch(); } - this._previousCategoryHoverActor = null; - this._previousSelectedAppActor = null; + this.hoveredCategory = null; + this.hoveredApp = null; this.closeContextMenu(false); this._disableVectorMask(); @@ -1434,17 +1410,19 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } _sidebarToggle() { - if (!this.sidebarShow) + if (!this.showSidebar) this.sidebar.hide(); else this.sidebar.show(); + this.updateNavigation(); } _bottomBoxToggle() { - if (!this.bottomBoxShow) + if (!this.showBottomBox) this.menuBottomBox.hide(); else this.menuBottomBox.show(); + this.updateNavigation(); } // Override js/applet.js so _updateIconAndLabel doesn't have to fight with size changes @@ -1657,19 +1635,19 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { Switches between left/right key presses */ if(St.Widget.get_default_direction() === St.TextDirection.RTL) { switch(symbol) { - case Clutter.KEY_Right: + case Clutter.KEY_Right: symbol = Clutter.KEY_Left; - break; - case Clutter.KEY_KP_Right: - symbol = Clutter.KEY_RP_Left; - break; - case Clutter.KEY_Left: - symbol = Clutter.KEY_Right; - break; + break; + case Clutter.KEY_KP_Right: + symbol = Clutter.KEY_RP_Left; + break; + case Clutter.KEY_Left: + symbol = Clutter.KEY_Right; + break; case Clutter.KEY_KP_Left: - symbol = Clutter.KEY_KP_Right; - break; - } + symbol = Clutter.KEY_KP_Right; + break; + } } /* check for a keybinding and quit early, otherwise we get a double hit @@ -1712,322 +1690,91 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { return true; } - let navigationKey = true; - let whichWay = "none"; if (this._activeContainer === null) { this._setKeyFocusToCurrentCategoryButton(); } + let iter = this._activeContainer._vis_iter; + let active = this._activeActor; switch (symbol) { case Clutter.KEY_Up: case Clutter.KEY_KP_Up: - whichWay = "up"; - if (this._activeContainer === this.favoriteAppsBox && ctrlKey && - this._activeActor._delegate instanceof FavoritesButton) - navigationKey = false; + item_actor = iter.prev(active); + if (iter.up && active === iter.first()) + item_actor = iter.up.last(); break; case Clutter.KEY_Down: case Clutter.KEY_KP_Down: - whichWay = "down"; - if (this._activeContainer === this.favoriteAppsBox && ctrlKey && - this._activeActor._delegate instanceof FavoritesButton) - navigationKey = false; + item_actor = iter.next(active); + if (iter.down && active === iter.last()) + item_actor = iter.down.first(); break; - case Clutter.KEY_Page_Up: - whichWay = "top"; break; - case Clutter.KEY_Page_Down: - whichWay = "bottom"; break; case Clutter.KEY_Right: case Clutter.KEY_KP_Right: - if (!this.searchActive) - whichWay = "right"; - if (this._activeContainer === this.applicationsBox) - whichWay = "none"; - else if (this._activeContainer === this.categoriesBox && this.noRecentDocuments && - this._activeActor._delegate.categoryId === "recent") - whichWay = "none"; + case Clutter.KEY_Tab: + if (!this.searchActive) { + item_actor = iter.right.first(); + if (iter.right === this.catBoxIter) + item_actor = this.hoveredCategory || iter.right.first(); + else if (iter.right === this.appBoxIter) + item_actor = this.hoveredApp || iter.right.first(); + if (this._activeContainer === this.systemButtonsBox) { + if (active === iter.last()) + item_actor = iter.right.first(); + else + item_actor = iter.next(active); + } + } break; case Clutter.KEY_Left: case Clutter.KEY_KP_Left: - if (!this.searchActive) - whichWay = "left"; - if (this._activeContainer === this.favoriteAppsBox || this._activeContainer === this.placesBox) - whichWay = "none"; - else if (!this.placesBox && this._activeContainer === this.categoriesBox) - whichWay = "none"; - break; - case Clutter.KEY_Tab: - if (!this.searchActive) - whichWay = "right"; - else - navigationKey = false; - break; case Clutter.KEY_ISO_Left_Tab: - if (!this.searchActive) - whichWay = "left"; - else - navigationKey = false; - break; - default: - navigationKey = false; - } - - if (navigationKey) { - switch (this._activeContainer) { - case this.categoriesBox: - switch (whichWay) { - case "up": - if (this._activeActor === this.catBoxIter.getFirstVisible()) { - item_actor = this.catBoxIter.getLastVisible(); - } else { - item_actor = this.catBoxIter.getPrevVisible(this._activeActor); - } - break; - case "down": - if (this._activeActor === this.catBoxIter.getLastVisible()) { - if (this.bottomBoxShow) - item_actor = this.sysBoxIter.getFirstVisible(); - else - item_actor = this.catBoxIter.getFirstVisible(); - } else { - item_actor = this.catBoxIter.getNextVisible(this._activeActor); - } - break; - case "right": - if (this._activeActor._delegate.categoryId === "recent" && - this.noRecentDocuments) { - if(this.sidebarShow) { - item_actor = this.placesBoxIter.getFirstVisible(); - } - } else { - item_actor = (this._previousSelectedAppActor != null) ? - this._previousSelectedAppActor : this.appBoxIter.getFirstVisible(); - } - break; - case "left": - if (this.placesBoxIter) { - item_actor = this.placesBoxIter.getFirstVisible(); - } else if (this._activeActor._delegate.categoryId != "recent" || - !this.noRecentDocuments) { - item_actor = (this._previousSelectedAppActor != null) ? - this._previousSelectedAppActor : - this.appBoxIter.getFirstVisible(); - } - break; - case "top": - item_actor = this.catBoxIter.getFirstVisible(); - break; - case "bottom": - item_actor = this.catBoxIter.getLastVisible(); - break; - } - break; - case this.applicationsBox: - switch (whichWay) { - case "up": - item_actor = this.appBoxIter.getPrevVisible(this._activeActor); - break; - case "down": - item_actor = this.appBoxIter.getNextVisible(this._activeActor); - break; - case "right": - if (this.sidebarShow) { - item_actor = this.placesBoxIter.getFirstVisible(); - } else { - item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - } - break; - case "left": - item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - break; - case "top": - item_actor = this.appBoxIter.getFirstVisible(); - break; - case "bottom": - item_actor = this.appBoxIter.getLastVisible(); - break; - } - break; - case this.favoriteAppsBox: - switch (whichWay) { - case "up": - if (this._activeActor === this.favBoxIter.getFirstVisible()) { - item_actor = this.placesBoxIter.getLastVisible(); - } else { - item_actor = this.favBoxIter.getPrevVisible(this._activeActor); - } - break; - case "down": - if (this._activeActor === this.favBoxIter.getLastVisible()) { - item_actor = this.placesBoxIter.getFirstVisible(); - } else { - item_actor = this.favBoxIter.getNextVisible(this._activeActor); - } - break; - case "right": - item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - break; - case "left": - /*item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - this._previousCategoryHoverActor = item_actor; - index = item_actor.get_parent()._vis_iter.getAbsoluteIndexOfChild(item_actor); - - this._buttonEnterEvent(item_actor._delegate);*/ - item_actor = (this._previousSelectedAppActor != null) ? - this._previousSelectedAppActor : - this.appBoxIter.getFirstVisible(); - break; - case "top": - item_actor = this.placesBoxIter.getFirstVisible(); - break; - case "bottom": - item_actor = this.favBoxIter.getLastVisible(); - break; - } - break; - case this.placesBox: - switch (whichWay) { - case "up": - if (this._activeActor === this.placesBoxIter.getFirstVisible()) { - item_actor = this.favBoxIter.getLastVisible(); - } else { - item_actor = this.placesBoxIter.getPrevVisible(this._activeActor); - } - break; - case "down": - if (this._activeActor === this.placesBoxIter.getLastVisible()) { - item_actor = this.favBoxIter.getFirstVisible(); - } else { - item_actor = this.placesBoxIter.getNextVisible(this._activeActor); - } - break; - case "right": - item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - break; - case "left": - /*item_actor = (this._previousCategoryHoverActor != null) ? - this._previousCategoryHoverActor : - this.catBoxIter.getFirstVisible(); - this._previousCategoryHoverActor = item_actor; - index = item_actor.get_parent()._vis_iter.getAbsoluteIndexOfChild(item_actor); - - this._buttonEnterEvent(item_actor._delegate);*/ - item_actor = (this._previousSelectedAppActor != null) ? - this._previousSelectedAppActor : - this.appBoxIter.getFirstVisible(); - break; - case "top": - item_actor = this.placesBoxIter.getFirstVisible(); - break; - case "bottom": - item_actor = this.favBoxIter.getLastVisible(); - break; + if (!this.searchActive) { + item_actor = iter.left.first(); + if (iter.left === this.catBoxIter) + item_actor = this.hoveredCategory || iter.left.first(); + else if (iter.left === this.appBoxIter) + item_actor = this.hoveredApp || iter.left.first(); + else if (iter.left === this.sysBoxIter) + item_actor = iter.left.last(); + if (this._activeContainer === this.systemButtonsBox) { + if (active === iter.first()) + item_actor = iter.left.last(); + else + item_actor = iter.prev(active); } - break; - case this.systemButtonsBox: - switch (whichWay) { - case "up": - item_actor = this.catBoxIter.getLastVisible(); - break; - case "right": - if (this._activeActor === this.sysBoxIter.getLastVisible()) { - item_actor = this.sysBoxIter.getFirstVisible(); - } else { - item_actor = this.sysBoxIter.getNextVisible(this._activeActor); - } - break; - case "left": - if (this._activeActor === this.sysBoxIter.getFirstVisible()) { - item_actor = this.sysBoxIter.getLastVisible(); - } else { - item_actor = this.sysBoxIter.getPrevVisible(this._activeActor); - } - break; - case "top": - item_actor = this.sysBoxIter.getFirstVisible(); - break; - case "bottom": - item_actor = this.sysBoxIter.getLastVisible(); - break; - } - break; - default: - break; - } - if (!item_actor) - return false; - } else { - if (this._activeContainer && (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter)) { - if (!ctrlKey) { - this._activeActor._delegate.activate(); - } else if (ctrlKey && this._activeContainer === this.applicationsBox) { - this.toggleContextMenu(this._activeActor._delegate); } - return true; - } else if (this._activeContainer === this.applicationsBox && symbol === Clutter.KEY_Menu) { - this.toggleContextMenu(this._activeActor._delegate); - return true; - } else if (!this.searchActive && this._activeContainer === this.favoriteAppsBox && symbol === Clutter.KEY_Delete) { - item_actor = this._activeActor; - const selectedItemIndex = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(this._activeActor); - if (item_actor._delegate instanceof FavoritesButton) { - let favorites = AppFavorites.getAppFavorites().getFavorites(); - let numFavorites = favorites.length; - AppFavorites.getAppFavorites().removeFavorite(item_actor._delegate.app.get_id()); - if (selectedItemIndex == (numFavorites-1)) - item_actor = this.favoriteAppsBox.get_child_at_index(selectedItemIndex-1); - else - item_actor = this.favoriteAppsBox.get_child_at_index(selectedItemIndex); + break; + case Clutter.KEY_Return: + case Clutter.KEY_KP_Enter: + if (ctrlKey && this._activeContainer === this.applicationsBox) { + this.toggleContextMenu(active._delegate); + return true; } - } else if (this._activeContainer === this.favoriteAppsBox && - (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down || - symbol === Clutter.KEY_Up || symbol === Clutter.KEY_KP_Up) && - ctrlKey && this._activeActor._delegate instanceof FavoritesButton) { - const selectedItemIndex = this._activeContainer._vis_iter.getAbsoluteIndexOfChild(this._activeActor); - item_actor = this._activeActor; - let id = item_actor._delegate.app.get_id(); - let appFavorites = AppFavorites.getAppFavorites(); - let favorites = appFavorites.getFavorites(); - let numFavorites = favorites.length; - let favPos = 0; - if (selectedItemIndex == (numFavorites-1) && (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down)) - favPos = 0; - else if (selectedItemIndex == 0 && (symbol === Clutter.KEY_Up || symbol === Clutter.KEY_KP_Up)) - favPos = numFavorites-1; - else if (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down) - favPos = selectedItemIndex + 1; - else - favPos = selectedItemIndex - 1; - appFavorites.moveFavoriteToPos(id, favPos); - item_actor = this.favoriteAppsBox.get_child_at_index(favPos); - } else if (symbol === Clutter.KEY_Tab || symbol === Clutter.KEY_ISO_Left_Tab) { - return true; - } else { - return false; - } + else { + active._delegate.activate(); + return true; + } + break; + case Clutter.KEY_Menu: + if (this._activeContainer === this.applicationsBox) { + this.toggleContextMenu(active._delegate); + return true; + } + default: + break; } if (!item_actor || item_actor === this.searchEntry) { return false; } - if (item_actor._delegate instanceof CategoryButton) { + if (item_actor._delegate instanceof CategoryButton) this._scrollToButton(item_actor._delegate, this.categoriesScrollBox); - } else if (item_actor._delegate instanceof FavoritesButton) { + else if (item_actor._delegate instanceof FavoritesButton) this._scrollToButton(item_actor._delegate, this.sidebarAppsScrollBox); - } else if (item_actor.get_parent() === this.applicationsBox) { + else if (item_actor.get_parent() === this.applicationsBox) this._scrollToButton(item_actor._delegate, this.applicationsScrollBox); - } this._buttonEnterEvent(item_actor._delegate); return true; @@ -2053,14 +1800,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { button.actor.add_style_pseudo_class("hover"); } } - this._previousCategoryHoverActor = button.actor; + this.hoveredCategory = button.actor; } else { const isFav = button instanceof FavoritesButton || button instanceof SystemButton || button instanceof PlaceButton; if (isFav) { button.actor.add_style_pseudo_class("hover"); } else { button.actor.set_style_class_name(`${button.styleClass}-selected`); - this._previousSelectedAppActor = button.actor; + this.hoveredApp = button.actor; } } @@ -2356,9 +2103,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { (new Gtk.RecentManager()).purge_items(); }; - if (!this.showApplicationIcons) - button.icon.visible = false; - this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.visible = this.menu.isOpen && this.lastSelectedCategory === "recent"; @@ -2736,6 +2480,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.sysBoxIter = new VisibleChildIterator(this.systemButtonsBox); this.systemButtonsBox._vis_iter = this.sysBoxIter; + this.updateNavigation(); + Mainloop.idle_add(() => { this._clearAllSelections(); this._hideAllAppActors(); @@ -2754,6 +2500,42 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._sidebarToggle(); } + updateNavigation() { + this.placesBoxIter.left = this.sysBoxIter; + this.placesBoxIter.right = this.catBoxIter; + this.placesBoxIter.up = this.favBoxIter; + this.placesBoxIter.down = this.favBoxIter; + this.favBoxIter.left = this.sysBoxIter; + this.favBoxIter.right = this.catBoxIter; + this.favBoxIter.up = this.placesBoxIter; + this.favBoxIter.down = this.placesBoxIter; + if (this.showSidebar) + this.catBoxIter.left = this.placesBoxIter; + else if (this.showBottomBox) + this.catBoxIter.left = this.sysBoxIter; + else + this.catBoxIter.left = this.appBoxIter; + this.catBoxIter.right = this.appBoxIter; + this.appBoxIter.left = this.catBoxIter; + if (this.showBottomBox) + this.appBoxIter.right = this.sysBoxIter; + else if (this.showSidebar) + this.appBoxIter.right = this.placesBoxIter; + else + this.appBoxIter.right = this.catBoxIter; + if (this.showBottomBox) + this.appBoxIter.right = this.sysBoxIter; + else if (this.showSidebar) + this.appBoxIter.right = this.placeBoxIter; + else + this.appBoxIter.right = this.catBoxIter; + this.sysBoxIter.left = this.appBoxIter; + if (this.showSidebar) + this.sysBoxIter.right = this.placesBoxIter; + else + this.sysBoxIter.right = this.catBoxIter; + } + _updateVFade() { let mag_on = this.a11y_settings.get_boolean("screen-magnifier-enabled") && this.a11y_mag_settings.get_double("mag-factor") > 1.0; @@ -3033,7 +2815,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._searchTimeoutId = 0; this._activeContainer = null; this._activeActor = null; - this._previousCategoryHoverActor = null; + this.hoveredCategory = null; var acResultButtons = []; // search box autocompletion results var buttons = [] @@ -3050,7 +2832,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (buttons.length || acResultButtons.length) { this.appBoxIter.reloadVisible(); - this._activeActor = this.appBoxIter.getFirstVisible(); + this._activeActor = this.appBoxIter.first(); this._activeContainer = this.applicationsBox; this._scrollToButton(this._activeActor._delegate); this._buttonEnterEvent(this._activeActor._delegate); @@ -3065,7 +2847,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.applicationsBox.add_actor(button.actor); if (this._activeActor === null) { this.appBoxIter.reloadVisible(); - this._activeActor = this.appBoxIter.getFirstVisible(); + this._activeActor = this.appBoxIter.first(); this._activeContainer = this.applicationsBox; if (this._activeActor && this._activeActor != this.searchEntry) { this._buttonEnterEvent(this._activeActor._delegate); diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index 90e53d1bf9..47aaba3dd6 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -26,8 +26,8 @@ "type" : "section", "title" : "Layout and content", "keys" : ["show-category-icons", - "category-icon-size", "show-application-icons", "application-icon-size", - "sidebar-show", "sidebar-icon-size", "bottombox-show", "show-recents", + "category-icon-size", "application-icon-size", + "show-sidebar", "sidebar-icon-size", "show-bottombox", "show-favorites", "show-recents", "show-description", "menu-editor-button", "reset-menu-size-button"] }, "menu-behave" : { @@ -88,12 +88,6 @@ "description" : "Categories icon size", "dependency" : "show-category-icons" }, - "show-application-icons" : { - "type" : "switch", - "default" : true, - "description" : "Show application icons", - "tooltip" : "Choose whether or not to show icons on applications." - }, "application-icon-size" : { "type": "spinbutton", "default" : 32, @@ -101,10 +95,9 @@ "max" : 48, "step" : 1, "units" : "px", - "description" : "Applications icon size", - "dependency" : "show-application-icons" + "description" : "Applications icon size" }, - "sidebar-show" : { + "show-sidebar" : { "type" : "switch", "default" : true, "description" : "Show the sidebar", @@ -118,9 +111,9 @@ "step" : 1, "units" : "px", "description" : "Sidebar icon size", - "dependency" : "sidebar-show" + "dependency" : "show-sidebar" }, - "bottombox-show" : { + "show-bottombox" : { "type" : "switch", "default" : true, "description" : "Show the bottom bar", From 5669c683b4356add0f2841a44e1125b6bd3d7066 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Sun, 28 Sep 2025 15:14:54 +0100 Subject: [PATCH 10/16] menu: Toggle between symbolics or fullcolor for categories This used to be an all or nothing preference. Turning it off allowed more focus on the apps but it made the category box tiny and not very good looking. This commit replaces this with subtle symbolic icons. They're small, they're all the same (same icon as for all-applications). The section looks good but doesn't draw the user's attention. This is the new default. --- .../applets/menu@cinnamon.org/applet.js | 36 ++++++++++++------- .../menu@cinnamon.org/settings-schema.json | 10 +++--- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index a077b4b0da..e0df6a1177 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -796,13 +796,22 @@ class CategoryButton extends SimpleMenuItem { }); this.actor.accessible_role = Atk.Role.LIST_ITEM; + let size = applet.categoryIconSize; + if (applet.symbolicCategoryIcons) { + size = 16; + symbolic = true; + if (categoryId == 'recent') + icon = 'folder-recent'; + else if (categoryId == 'favorite') + icon = 'xapp-user-favorites'; + else + icon = 'cinnamon-all-applications'; + } + if (typeof icon === 'string') - this.addIcon(applet.categoryIconSize, icon, null, symbolic); + this.addIcon(size, icon, null, symbolic); else if (icon) - this.addIcon(applet.categoryIconSize, null, icon, symbolic); - - if (this.icon && !applet.showCategoryIcons) - this.icon.visible = false; + this.addIcon(size, null, icon, symbolic); this.addLabel(this.name, 'menu-category-button-label'); @@ -1110,7 +1119,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("menu-icon-size", "menuIconSize", this._updateIconAndLabel); this.settings.bind("menu-label", "menuLabel", this._updateIconAndLabel); this.settings.bind("overlay-key", "overlayKey", this._updateKeybinding); - this.settings.bind("show-category-icons", "showCategoryIcons", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("symbolic-category-icons", "symbolicCategoryIcons", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("category-icon-size", "categoryIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("category-hover", "categoryHover", this._updateCategoryHover); this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); @@ -2160,8 +2169,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { * the remaining elements */ for (let i = this._categoryButtons.length - 1; i > -1; i--) { let b = this._categoryButtons[i]; - if (b === this._allAppsCategoryButton || - ['recent', 'favorite'].includes(b.categoryId)) + if (['recent', 'favorite'].includes(b.categoryId)) continue; this._categoryButtons[i].destroy(); this._categoryButtons.splice(i, 1); @@ -2170,11 +2178,15 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._applicationsButtons.forEach(button => button.destroy()); this._applicationsButtons = []; - if (!this._allAppsCategoryButton) { - this._allAppsCategoryButton = new CategoryButton(this, null, _("All Applications"), "cinnamon-all-applications", true); - this.categoriesBox.add_actor(this._allAppsCategoryButton.actor); - this._categoryButtons.push(this._allAppsCategoryButton); + const index = this._categoryButtons.findIndex(button => button.categoryId == null); + if (index !== -1) { + this._categoryButtons[index].destroy(); + this._categoryButtons.splice(index, 1); + this._allAppsCategoryButton = null; } + this._allAppsCategoryButton = new CategoryButton(this, null, _("All Applications"), "cinnamon-all-applications", true); + this.categoriesBox.add_actor(this._allAppsCategoryButton.actor); + this._categoryButtons.push(this._allAppsCategoryButton); // grab top level directories and all apps in them let [apps, dirs] = AppUtils.getApps(); diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index 47aaba3dd6..ba99693f6c 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -25,7 +25,7 @@ "menu-layout" : { "type" : "section", "title" : "Layout and content", - "keys" : ["show-category-icons", + "keys" : ["symbolic-category-icons", "category-icon-size", "application-icon-size", "show-sidebar", "sidebar-icon-size", "show-bottombox", "show-favorites", "show-recents", "show-description", "menu-editor-button", "reset-menu-size-button"] @@ -72,11 +72,11 @@ "tooltip" : "Enter custom text to show in the panel.", "dependency" : "menu-custom" }, - "show-category-icons" : { + "symbolic-category-icons" : { "type" : "switch", "default" : true, - "description" : "Show category icons", - "tooltip" : "Choose whether or not to show icons on categories." + "description" : "Use symbolic icons for categories", + "tooltip" : "Choose whether or not to use symbolic icons for categories." }, "category-icon-size" : { "type": "spinbutton", @@ -86,7 +86,7 @@ "step" : 1, "units" : "px", "description" : "Categories icon size", - "dependency" : "show-category-icons" + "dependency" : "!symbolic-category-icons" }, "application-icon-size" : { "type": "spinbutton", From f272876796cf089f660c5d8d96e6cbb6bb317cec Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Sun, 28 Sep 2025 15:26:28 +0100 Subject: [PATCH 11/16] menu: Color system buttons on hover --- data/theme/cinnamon-sass/widgets/_startmenu.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/theme/cinnamon-sass/widgets/_startmenu.scss b/data/theme/cinnamon-sass/widgets/_startmenu.scss index 2fb695f392..f363c1ba2f 100644 --- a/data/theme/cinnamon-sass/widgets/_startmenu.scss +++ b/data/theme/cinnamon-sass/widgets/_startmenu.scss @@ -71,7 +71,7 @@ $menu_outer_border_radius: $base_border_radius * 1.25; border-radius: 9999px; background-color: lighten($bg_color, 10%); - &:hover { background-color: lighten($bg_color, 15%); } + &:hover { background-color: $accent_color; } } } From f80e50a70a078472d6cc5c966046fb7f83138d3b Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Mon, 29 Sep 2025 13:48:17 +0100 Subject: [PATCH 12/16] menu: Move system buttons to the top This allows us to remove the bottom bar. --- .../cinnamon-sass/widgets/_startmenu.scss | 13 ++----- .../applets/menu@cinnamon.org/applet.js | 38 ++++--------------- .../menu@cinnamon.org/settings-schema.json | 8 +--- 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/data/theme/cinnamon-sass/widgets/_startmenu.scss b/data/theme/cinnamon-sass/widgets/_startmenu.scss index f363c1ba2f..e4d1bc2dc3 100644 --- a/data/theme/cinnamon-sass/widgets/_startmenu.scss +++ b/data/theme/cinnamon-sass/widgets/_startmenu.scss @@ -55,18 +55,13 @@ $menu_outer_border_radius: $base_border_radius * 1.25; } } - &-bottom-box { - padding: $menu_outer_padding; - border-radius: 0 0 $menu_outer_border_radius 0; - border-top-width: 1px; - border-color: $borders_color; - } - &-system-buttons-box { - spacing: 12px; + spacing: 6px; + padding-left: 6px; + padding-right: 6px; .system-button { - padding: $base_padding * 1.5; + padding: $base_padding * 1.4; border: 1px solid $borders_color; border-radius: 9999px; background-color: lighten($bg_color, 10%); diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index e0df6a1177..12e9082c24 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -1126,7 +1126,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("show-description", "showDescription", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("show-sidebar", "showSidebar", this._sidebarToggle); this.settings.bind("sidebar-icon-size", "sidebarIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); - this.settings.bind("show-bottombox", "showBottomBox", this._bottomBoxToggle); this.settings.bind("enable-animation", "enableAnimation", null); this.settings.bind("popup-width", "popup_width"); this.settings.bind("popup-height", "popup_height"); @@ -1426,14 +1425,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.updateNavigation(); } - _bottomBoxToggle() { - if (!this.showBottomBox) - this.menuBottomBox.hide(); - else - this.menuBottomBox.show(); - this.updateNavigation(); - } - // Override js/applet.js so _updateIconAndLabel doesn't have to fight with size changes // from the panel configuration. This gets called any time set_applet_icon() variants are // called. @@ -2275,7 +2266,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { } }; - this.systemButtonsBox.add(button.actor, { y_align: St.Align.END, y_fill: false }); + this.systemButtonsBox.add(button.actor, { y_align: St.Align.MIDDLE, y_fill: false }); //Logout button button = new SystemButton(this, "system-log-out", @@ -2287,7 +2278,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._session.LogoutRemote(0); }; - this.systemButtonsBox.add(button.actor, { y_align: St.Align.END, y_fill: false }); + this.systemButtonsBox.add(button.actor, { y_align: St.Align.MIDDLE, y_fill: false }); //Shutdown button button = new SystemButton(this, "system-shutdown", @@ -2299,7 +2290,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._session.ShutdownRemote(); }; - this.systemButtonsBox.add(button.actor, { y_align: St.Align.END, y_fill: false }); + this.systemButtonsBox.add(button.actor, { y_align: St.Align.MIDDLE, y_fill: false }); } _scrollToButton(button, scrollBox = this.applicationsScrollBox) { @@ -2464,22 +2455,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesApplicationsBox.actor.add(this.applicationsScrollBox, {span: -1, expand: true}); - this.menuBottomBox = new St.BoxLayout({ - style_class: 'menu-bottom-box', - y_expand: false, - x_expand: true, - x_align: Clutter.ActorAlign.FILL - }); - this.systemButtonsBox = new St.BoxLayout({ style_class: 'menu-system-buttons-box', y_expand: false, - x_expand: true, + x_expand: false, x_align: Clutter.ActorAlign.END }); - this.right_box.add(this.menuBottomBox); - this.menuBottomBox.add(this.systemButtonsBox); + this.searchBox.add(this.systemButtonsBox); this.appBoxIter = new VisibleChildIterator(this.applicationsBox); this.applicationsBox._vis_iter = this.appBoxIter; @@ -2508,7 +2491,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("enable-autoscroll", "autoscroll_enabled", this._update_autoscroll); this._update_autoscroll(); - this._bottomBoxToggle(); this._sidebarToggle(); } @@ -2523,21 +2505,15 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.favBoxIter.down = this.placesBoxIter; if (this.showSidebar) this.catBoxIter.left = this.placesBoxIter; - else if (this.showBottomBox) - this.catBoxIter.left = this.sysBoxIter; else this.catBoxIter.left = this.appBoxIter; this.catBoxIter.right = this.appBoxIter; this.appBoxIter.left = this.catBoxIter; - if (this.showBottomBox) - this.appBoxIter.right = this.sysBoxIter; - else if (this.showSidebar) + if (this.showSidebar) this.appBoxIter.right = this.placesBoxIter; else this.appBoxIter.right = this.catBoxIter; - if (this.showBottomBox) - this.appBoxIter.right = this.sysBoxIter; - else if (this.showSidebar) + if (this.showSidebar) this.appBoxIter.right = this.placeBoxIter; else this.appBoxIter.right = this.catBoxIter; diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index ba99693f6c..cc8a1ddc34 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -27,7 +27,7 @@ "title" : "Layout and content", "keys" : ["symbolic-category-icons", "category-icon-size", "application-icon-size", - "show-sidebar", "sidebar-icon-size", "show-bottombox", "show-favorites", "show-recents", + "show-sidebar", "sidebar-icon-size", "show-favorites", "show-recents", "show-description", "menu-editor-button", "reset-menu-size-button"] }, "menu-behave" : { @@ -113,12 +113,6 @@ "description" : "Sidebar icon size", "dependency" : "show-sidebar" }, - "show-bottombox" : { - "type" : "switch", - "default" : true, - "description" : "Show the bottom bar", - "tooltip" : "Choose whether or not to show the bottom bar of the menu." - }, "show-favorites" : { "type" : "switch", "default" : true, From d2f24f3d97b8ca9eec561d90143c3f4d27500049 Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Tue, 30 Sep 2025 09:05:34 +0100 Subject: [PATCH 13/16] menu: Make places/bookmarks configurable This is important because: - distributions want to be able to fill the menu with a number of favs and places which fit nicely in the default menu size. - Users can have a lot of bookmarks and might not want to see them in the menu, but special dirs are bookmarks, so it's all or nothing.. - Some special dirs are more important than others, based on preference, for instance you might never use Music, but use Downloads all the time, other people don't.. - Just like distributions, users will want to fill their sidebar with places and favs with the right mix and the right number of items which' fits nicely. Having the ability to decide what is shown and what isn't lets them do just that. --- .../applets/menu@cinnamon.org/applet.js | 96 +++++++++++---- .../menu@cinnamon.org/settings-schema.json | 112 ++++++++++++++---- 2 files changed, 158 insertions(+), 50 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 12e9082c24..8c000dc1f6 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -1125,6 +1125,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("application-icon-size", "applicationIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("show-description", "showDescription", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("show-sidebar", "showSidebar", this._sidebarToggle); + this.settings.bind("show-home", "showHome", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-desktop", "showDesktop", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-downloads", "showDownloads", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-documents", "showDocuments", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-music", "showMusic", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-pictures", "showPictures", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-videos", "showVideos", () => this.queueRefresh(REFRESH_ALL_MASK)); + this.settings.bind("show-bookmarks", "showBookmarks", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("sidebar-icon-size", "sidebarIconSize", () => this.queueRefresh(REFRESH_ALL_MASK)); this.settings.bind("enable-animation", "enableAnimation", null); this.settings.bind("popup-width", "popup_width"); @@ -1250,14 +1258,14 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let m = this.refreshMask; if ((m & RefreshFlags.APP) === RefreshFlags.APP) this._refreshApps(); - if ((m & RefreshFlags.FAV_APP) === RefreshFlags.FAV_APP) - this._refreshFavApps(); if ((m & RefreshFlags.SYSTEM) === RefreshFlags.SYSTEM) this._refreshSystemButtons(); if ((m & RefreshFlags.FAV_DOC) === RefreshFlags.FAV_DOC) this._refreshFavDocs(); if ((m & RefreshFlags.PLACE) === RefreshFlags.PLACE) this._refreshPlaces(); + if ((m & RefreshFlags.FAV_APP) === RefreshFlags.FAV_APP) + this._refreshFavApps(); if ((m & RefreshFlags.RECENT) === RefreshFlags.RECENT) this._refreshRecent(); @@ -1739,7 +1747,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { item_actor = iter.left.last(); if (this._activeContainer === this.systemButtonsBox) { if (active === iter.first()) - item_actor = iter.left.last(); + item_actor = iter.left.first(); else item_actor = iter.prev(active); } @@ -2048,8 +2056,43 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { //Load places again this._placesButtons = []; - let places = [...Main.placesManager.getDefaultPlaces(), ...Main.placesManager.getBookmarks()] + let places = [...Main.placesManager.getDefaultPlaces(), ...Main.placesManager.getBookmarks()]; for (let place of places) { + let path = place.idDecoded.replace("bookmark:file://", "") + switch (path) { + case "special:home": + if (!this.showHome) + continue; + break; + case "special:desktop": + if (!this.showDesktop) + continue; + break; + case GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD): + if (!this.showDownloads) + continue; + break; + case GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS): + if (!this.showDocuments) + continue; + break; + case GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC): + if (!this.showMusic) + continue; + break; + case GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES): + if (!this.showPictures) + continue; + break; + case GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS): + if (!this.showVideos) + continue; + break; + default: + if (!this.showBookmarks) + continue; + break; + } let button = new PlaceButton(this, place); this._placesButtons.push(button); this.placesBox.add(button.actor, { @@ -2057,6 +2100,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { y_fill: false }); } + // Update navigation because the presence/absence of places + // can impact it. + this.updateNavigation(); } _refreshRecent () { @@ -2221,9 +2267,10 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.favoriteAppsBox.destroy_all_children(); //Load favorites again addding the separator first - let separator = new PopupMenu.PopupSeparatorMenuItem(); - this.favoriteAppsBox.add_actor(separator.actor); - + if (this._placesButtons.length > 0) { + let separator = new PopupMenu.PopupSeparatorMenuItem(); + this.favoriteAppsBox.add_actor(separator.actor); + } this._favoriteAppButtons = []; let launchers = global.settings.get_strv('favorite-apps'); for (let launcher of launchers) { @@ -2501,27 +2548,26 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.placesBoxIter.down = this.favBoxIter; this.favBoxIter.left = this.sysBoxIter; this.favBoxIter.right = this.catBoxIter; - this.favBoxIter.up = this.placesBoxIter; - this.favBoxIter.down = this.placesBoxIter; - if (this.showSidebar) - this.catBoxIter.left = this.placesBoxIter; - else - this.catBoxIter.left = this.appBoxIter; + this.favBoxIter.up = null; + this.favBoxIter.down = null; this.catBoxIter.right = this.appBoxIter; + this.catBoxIter.left = this.sysBoxIter; this.appBoxIter.left = this.catBoxIter; - if (this.showSidebar) - this.appBoxIter.right = this.placesBoxIter; - else - this.appBoxIter.right = this.catBoxIter; - if (this.showSidebar) - this.appBoxIter.right = this.placeBoxIter; - else - this.appBoxIter.right = this.catBoxIter; + this.appBoxIter.right = this.sysBoxIter; this.sysBoxIter.left = this.appBoxIter; - if (this.showSidebar) - this.sysBoxIter.right = this.placesBoxIter; - else - this.sysBoxIter.right = this.catBoxIter; + this.sysBoxIter.right = this.catBoxIter; + if (this.showSidebar) { + if (this._placesButtons.length > 0) { + this.favBoxIter.up = this.placesBoxIter; + this.favBoxIter.down = this.placesBoxIter; + this.catBoxIter.left = this.placesBoxIter; + this.sysBoxIter.right = this.placesBoxIter; + } + else { + this.catBoxIter.left = this.favBoxIter; + this.sysBoxIter.right = this.favBoxIter; + } + } } _updateVFade() { diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json index cc8a1ddc34..1c0613ed5d 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/settings-schema.json @@ -1,39 +1,53 @@ { - "layout" : { + "layout1" : { "type" : "layout", - "pages" : ["panel", "menu"], - "panel" : { + "pages" : ["layout", "appearance", "behavior"], + "layout" : { "type" : "page", - "title" : "Panel", - "sections" : ["panel-appear", "panel-behave"] + "title" : "Layout", + "sections" : ["layout-places", "layout-content"] }, - "menu" : { + "appearance" : { "type" : "page", - "title" : "Menu", - "sections" : ["menu-layout", "menu-behave"] + "title" : "Appearance", + "sections" : ["appearance-menu"] + }, + "behavior" : { + "type" : "page", + "title" : "Behavior", + "sections" : ["behavior-menu", "behavior-panel"] }, - "panel-appear" : { + "behavior-menu" : { "type" : "section", - "title" : "Appearance", - "keys" : ["menu-custom", "menu-icon", "menu-icon-size", "menu-label"] + "title" : "Menu", + "keys" : ["category-hover", "enable-autoscroll"] }, - "panel-behave" : { + "behavior-panel" : { "type" : "section", - "title" : "Behavior", + "title" : "Panel", "keys" : ["overlay-key", "activate-on-hover", "hover-delay", "force-show-panel", "enable-animation"] }, - "menu-layout" : { + "appearance-menu" : { "type" : "section", - "title" : "Layout and content", - "keys" : ["symbolic-category-icons", - "category-icon-size", "application-icon-size", - "show-sidebar", "sidebar-icon-size", "show-favorites", "show-recents", - "show-description", "menu-editor-button", "reset-menu-size-button"] + "title" : "Menu", + "keys" : ["show-description", "symbolic-category-icons", "category-icon-size", + "application-icon-size", "sidebar-icon-size", + "reset-menu-size-button"] }, - "menu-behave" : { + "appearance-panel" : { "type" : "section", - "title" : "Behavior", - "keys" : ["category-hover", "enable-autoscroll"] + "title" : "Panel", + "keys" : ["menu-custom", "menu-icon", "menu-icon-size", "menu-label"] + }, + "layout-places" : { + "type" : "section", + "title" : "Places", + "keys" : ["show-home", "show-desktop", "show-documents", "show-downloads", "show-music", "show-pictures", "show-videos", "show-bookmarks"] + }, + "layout-content" : { + "type" : "section", + "title" : "Content", + "keys" : ["show-sidebar", "show-favorites", "show-recents", "menu-editor-button"] } }, "overlay-key" : { @@ -100,9 +114,57 @@ "show-sidebar" : { "type" : "switch", "default" : true, - "description" : "Show the sidebar", + "description" : "Sidebar", "tooltip" : "Choose whether or not to show the left pane of the menu." }, +"show-home" : { + "type" : "switch", + "default" : true, + "description" : "Home", + "tooltip" : "Choose whether or not to show the home directory." + }, +"show-desktop" : { + "type" : "switch", + "default" : true, + "description" : "Desktop", + "tooltip" : "Choose whether or not to show the desktop." + }, +"show-documents" : { + "type" : "switch", + "default" : true, + "description" : "Documents", + "tooltip" : "Choose whether or not to show the documents folder." + }, +"show-downloads" : { + "type" : "switch", + "default" : true, + "description" : "Downloads", + "tooltip" : "Choose whether or not to show the downloads folder." + }, + "show-music" : { + "type" : "switch", + "default" : true, + "description" : "Music", + "tooltip" : "Choose whether or not to show the music folder." + }, +"show-pictures" : { + "type" : "switch", + "default" : true, + "description" : "Pictures", + "tooltip" : "Choose whether or not to show the pictures folder." + }, + "show-videos" : { + "type" : "switch", + "default" : true, + "description" : "Videos", + "tooltip" : "Choose whether or not to show the videos folder." + }, + "show-bookmarks" : { + "type" : "switch", + "default" : true, + "description" : "Other bookmarks", + "tooltip" : "Choose whether or not to show other bookmarks." + }, "sidebar-icon-size" : { "type": "spinbutton", "default" : 24, @@ -116,13 +178,13 @@ "show-favorites" : { "type" : "switch", "default" : true, - "description": "Show favorites", + "description": "Favorites", "tooltip": "Choose whether or not to show favorite files in the menu." }, "show-recents" : { "type" : "switch", "default" : true, - "description": "Show recents", + "description": "Recents", "tooltip": "Choose whether or not to show recents in the menu." }, "show-description" : { From 8a4860a00e1f89247006fc682c29c4d06283bcad Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Mon, 29 Sep 2025 16:03:11 +0100 Subject: [PATCH 14/16] menu: Hide scrollbars in sidebar/categories --- .../theme/cinnamon-sass/widgets/_startmenu.scss | 5 ----- .../applets/menu@cinnamon.org/applet.js | 17 ++--------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/data/theme/cinnamon-sass/widgets/_startmenu.scss b/data/theme/cinnamon-sass/widgets/_startmenu.scss index e4d1bc2dc3..6efb817e80 100644 --- a/data/theme/cinnamon-sass/widgets/_startmenu.scss +++ b/data/theme/cinnamon-sass/widgets/_startmenu.scss @@ -29,11 +29,6 @@ $menu_outer_border_radius: $base_border_radius * 1.25; padding-bottom: $menu_outer_padding / 2; } - StScrollBar StButton#vhandle { - // add some extra spacing at the top to align this with the separator - margin-top: $menu_outer_padding; - } - .menu-favorites-button { @extend %start_menu_button; diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 8c000dc1f6..946df15ad1 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -1332,24 +1332,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _setMenuSize(width, height) { this.main_container.natural_height = (height * global.ui_scale); this.main_container.natural_width = (width * global.ui_scale); - - this._update_scroll_policy(this.sidebarAppsBox, this.sidebarAppsScrollBox); - this._update_scroll_policy(this.categoriesBox, this.categoriesScrollBox); - this._size_dirty = false; } - _update_scroll_policy(box, scrollview) { - let h = box.get_preferred_height(-1)[1]; - let alloc = box.get_allocation_box(); - - if (alloc.y2 - alloc.y1 < h) { - scrollview.vscroll.visible = true; - } else { - scrollview.vscroll.visible = false; - } - } - on_orientation_changed (orientation) { this._updateIconAndLabel(); this._size_dirty = true; @@ -2392,6 +2377,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { }); this.sidebarAppsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.sidebarAppsScrollBox.add_actor(this.sidebarAppsBox); + this.sidebarAppsScrollBox.get_vscroll_bar().hide(); this.sidebar.add(this.sidebarAppsScrollBox, { expand: true }); @@ -2472,6 +2458,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesScrollBox.add_actor(this.categoriesBox); this.categoriesScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.categoriesScrollBox.set_clip_to_allocation(true); + this.categoriesScrollBox.get_vscroll_bar().hide(); this.categoriesApplicationsBox.actor.add(this.categoriesScrollBox); From b450058ee5134b1513a649d5fbb9ec7b8570f86e Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Tue, 30 Sep 2025 11:43:40 +0100 Subject: [PATCH 15/16] menu: Disable vfade on sidebar/categories I can't manage to fix vfade issues when it's used on multiple scrollviews: - Scrolling the apps leads to the top of sidebar/category scrollviews to shade randomly - Random rendering issues on shade zones when hovering the categories vfade would look nice everywhere but these issues make the menu experience worst, not better. Disable these until we find a proper fix. --- .../share/cinnamon/applets/menu@cinnamon.org/applet.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 946df15ad1..4d1dc5c5fd 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -2373,7 +2373,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.sidebarAppsBox = new SidebarAppsBox().actor; this.sidebarAppsScrollBox = new St.ScrollView({ y_align: St.Align.START, - style_class: 'vfade menu-sidebar-scrollbox' + style_class: 'menu-sidebar-scrollbox' }); this.sidebarAppsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.sidebarAppsScrollBox.add_actor(this.sidebarAppsBox); @@ -2454,7 +2454,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { accessible_role: Atk.Role.LIST }); - this.categoriesScrollBox = new St.ScrollView({ style_class: 'vfade menu-applications-scrollbox' }); + this.categoriesScrollBox = new St.ScrollView({ style_class: 'menu-categories-scrollbox' }); this.categoriesScrollBox.add_actor(this.categoriesBox); this.categoriesScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.categoriesScrollBox.set_clip_to_allocation(true); @@ -2562,12 +2562,8 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.a11y_mag_settings.get_double("mag-factor") > 1.0; if (mag_on) { this.applicationsScrollBox.style_class = "menu-applications-scrollbox"; - this.categoriesScrollBox.style_class = "menu-applications-scrollbox"; - this.sidebarAppsScrollBox.style_class = "menu-favorites-scrollbox"; } else { this.applicationsScrollBox.style_class = "vfade menu-applications-scrollbox"; - this.categoriesScrollBox.style_class = "vfade menu-applications-scrollbox"; - this.sidebarAppsScrollBox.style_class = "vfade menu-favorites-scrollbox"; } } From c0159e7e1d6897f5059a37511d48aa885bb7542c Mon Sep 17 00:00:00 2001 From: Clement Lefebvre Date: Tue, 30 Sep 2025 15:51:11 +0100 Subject: [PATCH 16/16] menu: Use new CSS names Use different class names than the old menu. That way we can update themes with the new style section without having to affect the section for the old menu. This means the theme can be updated for both new and old cinnamon versions, without having to get versioned. If people fork the old menu and bring it to spices, themes will continue to work with it without needing it to get new class names and support. --- data/theme/cinnamon-sass/_widgets.scss | 1 + .../cinnamon-sass/widgets/_deprecated.scss | 98 +++++++++++++++++++ .../cinnamon-sass/widgets/_startmenu.scss | 59 +++++------ .../applets/menu@cinnamon.org/applet.js | 96 +++++++++--------- 4 files changed, 171 insertions(+), 83 deletions(-) create mode 100644 data/theme/cinnamon-sass/widgets/_deprecated.scss diff --git a/data/theme/cinnamon-sass/_widgets.scss b/data/theme/cinnamon-sass/_widgets.scss index 787e2b7159..4995d66338 100644 --- a/data/theme/cinnamon-sass/_widgets.scss +++ b/data/theme/cinnamon-sass/_widgets.scss @@ -15,3 +15,4 @@ @import 'widgets/startmenu'; @import 'widgets/switch-check'; @import 'widgets/windowlist'; +@import 'widgets/deprecated'; diff --git a/data/theme/cinnamon-sass/widgets/_deprecated.scss b/data/theme/cinnamon-sass/widgets/_deprecated.scss new file mode 100644 index 0000000000..c79a776bd9 --- /dev/null +++ b/data/theme/cinnamon-sass/widgets/_deprecated.scss @@ -0,0 +1,98 @@ +// OLD MENU - Used by some of the menu spices + +$menu_outer_padding: 9px; +$menu_outer_border_radius: $base_border_radius * 1.25; + +%start_menu_button { + padding: $base_padding; + spacing: $base_margin; + border-radius: $base_border_radius; +} + +.menu { + + // this can be used to color the entire menu background but we aren't using it here + &-background {} + + &-favorites-box { + padding: $base_padding * 1.5; + background-color: $light_bg_color; + border: 1px solid transparentize(black, 0.9); + border-radius: $base_border_radius; + } + + &-favorites-button { + padding: $base_padding * 1.5; + border-radius: $base_border_radius; + + &:hover { background-color: $lightest_bg_color; } + } + + &-categories-box { padding: $menu_outer_padding $base_padding * 4; } + &-applications-box { padding: $menu_outer_padding; } + &-applications-inner-box { + &:ltr { padding-left: $base_padding * 4; } + &:rtl { padding-right: $base_padding * 4; } + } + + &-application-button { + @extend %start_menu_button; + + &-label { padding: 0; } + + &:highlighted { font-weight: bold; } + + &-selected { + @extend %start_menu_button; + + background-color: $light_bg_color; + + &:highlighted { font-weight: bold; } + } + } + + &-category-button { + @extend %start_menu_button; + + &-label { padding: 0; } + + &:hover { background-color: $lighter_bg_color; } + + &-selected { + @extend %start_menu_button; + + background-color: $light_bg_color; + } + + &-greyed { + @extend %start_menu_button; + color: $insensitive_fg_color; + font-style: italic; + + StIcon { opacity: 0.5; } + } + } + + &-context-menu {} + + &-selected-app-box { + padding-right: $base_padding * 4; + padding-left: $base_padding * 4; + text-align: right; + } + + &-selected-app-title { @extend %heading; } + &-selected-app-description { color: $insensitive_fg_color; } + + &-search-box { + padding: 0 0 $base_padding $menu_outer_padding; + } +} + +#menu-search-entry { + @extend %entry; +} + +.menu-search-entry-icon { + icon-size: $scalable_icon_size; +} diff --git a/data/theme/cinnamon-sass/widgets/_startmenu.scss b/data/theme/cinnamon-sass/widgets/_startmenu.scss index 6efb817e80..1cfeaeea54 100644 --- a/data/theme/cinnamon-sass/widgets/_startmenu.scss +++ b/data/theme/cinnamon-sass/widgets/_startmenu.scss @@ -1,13 +1,13 @@ -$menu_outer_padding: 9px; -$menu_outer_border_radius: $base_border_radius * 1.25; +$appmenu_outer_padding: 9px; +$appmenu_outer_border_radius: $base_border_radius * 1.25; -%start_menu_button { +%appmenu_button { padding: $base_padding; spacing: $base_margin; border-radius: $base_border_radius; } -.menu { +.appmenu { // this can be used to color the entire menu background but we aren't using it here &-background { @@ -20,34 +20,26 @@ $menu_outer_border_radius: $base_border_radius * 1.25; &-sidebar { background-color: $base_color; - border-radius: $menu_outer_border_radius 0 0 $menu_outer_border_radius; + border-radius: $appmenu_outer_border_radius 0 0 $appmenu_outer_border_radius; border-right-width: 1px; border-color: $borders_color; .sidebar-user-box { - padding-top: $menu_outer_padding; - padding-bottom: $menu_outer_padding / 2; + padding-top: $appmenu_outer_padding; + padding-bottom: $appmenu_outer_padding / 2; } - .menu-favorites-button { - @extend %start_menu_button; + .appmenu-favorites-button { + @extend %appmenu_button; padding: 3px $base_padding 3px $base_padding; - margin: 0 $menu_outer_padding 0 $menu_outer_padding; + margin: 0 $appmenu_outer_padding 0 $appmenu_outer_padding; &:hover { background-color: $lighter_bg_color; border-color: $borders_color; } } - - .popup-separator-menu-item { - -gradient-height: 1px; - -gradient-start: lighten($base_color, 5%); - -gradient-end: lighten($base_color, 5%); - -margin-horizontal: 0; - height: 1px; - } } &-system-buttons-box { @@ -65,27 +57,25 @@ $menu_outer_border_radius: $base_border_radius * 1.25; } } - &-categories-box { padding: $menu_outer_padding $base_padding * 3; } - &-applications-box { padding: $menu_outer_padding; } + &-categories-box { padding: $appmenu_outer_padding $base_padding * 3; } + &-applications-box { padding: $appmenu_outer_padding; } // This small amount of padding is needed to be able // to resize the menu with the mouse &-applications-scrollbox { padding-right: 3px; } &-application-button { - @extend %start_menu_button; + @extend %appmenu_button; &-label { padding: 0; } &-description { @extend %caption; - color: $light_text_color; } &:highlighted { font-weight: bold; } &-selected { - @extend %start_menu_button; - + @extend %appmenu_button; background-color: $light_bg_color; &:highlighted { font-weight: bold; } @@ -93,20 +83,20 @@ $menu_outer_border_radius: $base_border_radius * 1.25; } &-category-button { - @extend %start_menu_button; + @extend %appmenu_button; &-label { padding: 0; } &:hover { background-color: $lighter_bg_color; } &-selected { - @extend %start_menu_button; + @extend %appmenu_button; background-color: $light_bg_color; } &-greyed { - @extend %start_menu_button; + @extend %appmenu_button; color: $insensitive_fg_color; font-style: italic; @@ -117,18 +107,17 @@ $menu_outer_border_radius: $base_border_radius * 1.25; &-context-menu {} &-search-box { - padding: $base_padding $base_padding $base_padding $menu_outer_padding; + padding: $base_padding $base_padding $base_padding $appmenu_outer_padding; border-bottom-width: 1px; border-color: $borders_color; } -} -#menu-search-entry { - @extend %entry; - - margin: $menu_outer_padding; + &-search-entry-icon { + icon-size: $scalable_icon_size; + } } -.menu-search-entry-icon { - icon-size: $scalable_icon_size; +#appmenu-search-entry { + @extend %entry; + margin: $appmenu_outer_padding; } diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index 4d1dc5c5fd..421e921f23 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -500,7 +500,7 @@ class TransientButton extends SimpleMenuItem { super(applet, { description: pathOrCommand, type: 'transient', - styleClass: 'menu-application-button', + styleClass: 'appmenu-application-button', }); if (pathOrCommand.startsWith('~')) { @@ -544,7 +544,7 @@ class TransientButton extends SimpleMenuItem { this.addActor(this.icon); - this.addLabel(this.description, 'menu-application-button-label'); + this.addLabel(this.description, 'appmenu-application-button-label'); this.isDraggableApp = false; } @@ -567,7 +567,7 @@ class TransientButton extends SimpleMenuItem { class ApplicationButton extends GenericApplicationButton { constructor(applet, app) { - super(applet, app, 'app', true, 'menu-application-button'); + super(applet, app, 'app', true, 'appmenu-application-button'); this.category = []; this.icon = this.app.create_icon_texture(applet.applicationIconSize); let gicon = this.icon.get_gicon(); @@ -585,9 +585,9 @@ class ApplicationButton extends GenericApplicationButton { } this.addActor(this.icon); - this.addLabel(this.name, 'menu-application-button-label'); + this.addLabel(this.name, 'appmenu-application-button-label'); if (applet.showDescription) - this.addDescription(this.description, 'menu-application-button-description'); + this.addDescription(this.description, 'appmenu-application-button-description'); this._draggable = DND.makeDraggable(this.actor); this._signals.connect(this._draggable, 'drag-end', this._onDragEnd.bind(this)); @@ -631,7 +631,7 @@ class SearchProviderResultButton extends SimpleMenuItem { name:result.label, description: result.description, type: 'search-provider', - styleClass: 'menu-application-button', + styleClass: 'appmenu-application-button', provider: provider, result: result, }); @@ -650,7 +650,7 @@ class SearchProviderResultButton extends SimpleMenuItem { if (this.icon) this.addActor(this.icon); - this.addLabel(result.label, 'menu-application-button-label'); + this.addLabel(result.label, 'appmenu-application-button-label'); } activate() { @@ -683,7 +683,7 @@ class PlaceButton extends SimpleMenuItem { super(applet, { name: place.name, type: 'place', - styleClass: 'menu-favorites-button', + styleClass: 'appmenu-favorites-button', place: place, }); @@ -693,7 +693,7 @@ class PlaceButton extends SimpleMenuItem { else this.addIcon(applet.applicationIconSize, 'folder'); - this.addLabel(this.name, 'menu-application-button-label'); + this.addLabel(this.name, 'appmenu-application-button-label'); } activate() { @@ -711,7 +711,7 @@ class RecentButton extends SimpleMenuItem { name: recent.name, description: path, type: 'recent', - styleClass: 'menu-application-button', + styleClass: 'appmenu-application-button', withMenu: true, uri: recent.uri, }); @@ -719,9 +719,9 @@ class RecentButton extends SimpleMenuItem { this.icon = recent.createIcon(applet.applicationIconSize); this.addActor(this.icon); - this.addLabel(this.name, 'menu-application-button-label'); + this.addLabel(this.name, 'appmenu-application-button-label'); if (applet.showDescription) - this.addDescription(this.description, 'menu-application-button-description'); + this.addDescription(this.description, 'appmenu-application-button-description'); this.searchStrings = [ AppUtils.decomp_string(recent.name).replace(/\s/g, '') @@ -751,7 +751,7 @@ class PathButton extends SimpleMenuItem { name: name, description: shorten_path(uri, name), type: type, - styleClass: 'menu-application-button', + styleClass: 'appmenu-application-button', withMenu: false, uri: uri, }); @@ -759,10 +759,10 @@ class PathButton extends SimpleMenuItem { this.icon = icon; this.addActor(this.icon); - this.addLabel(name, 'menu-application-button-label'); + this.addLabel(name, 'appmenu-application-button-label'); if (applet.showDescription) - this.addDescription(this.description, 'menu-application-button-description'); + this.addDescription(this.description, 'appmenu-application-button-description'); this.searchStrings = [ AppUtils.decomp_string(name).replace(/\s/g, '') @@ -791,7 +791,7 @@ class CategoryButton extends SimpleMenuItem { super(applet, { name: label, type: 'category', - styleClass: 'menu-category-button', + styleClass: 'appmenu-category-button', categoryId: categoryId, }); this.actor.accessible_role = Atk.Role.LIST_ITEM; @@ -813,7 +813,7 @@ class CategoryButton extends SimpleMenuItem { else if (icon) this.addIcon(size, null, icon, symbolic); - this.addLabel(this.name, 'menu-category-button-label'); + this.addLabel(this.name, 'appmenu-category-button-label'); this.actor_motion_id = 0; } @@ -824,14 +824,14 @@ class CategoryButton extends SimpleMenuItem { this.applet._select_category(this.categoryId); this.applet.hoveredApp = null; this.applet.categoriesBox.get_children().forEach(child => - child.set_style_class_name("menu-category-button")); - this.actor.style_class = "menu-category-button-selected"; + child.set_style_class_name("appmenu-category-button")); + this.actor.style_class = "appmenu-category-button-selected"; } } class FavoritesButton extends GenericApplicationButton { constructor(applet, app) { - super(applet, app, 'fav', false, 'menu-favorites-button'); + super(applet, app, 'fav', false, 'appmenu-favorites-button'); this.icon = app.create_icon_texture(applet.sidebarIconSize); this.addActor(this.icon); this.addLabel(this.name); @@ -1107,7 +1107,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.settings.bind("activate-on-hover", "activateOnHover", this._updateActivateOnHover); this._updateActivateOnHover(); - this.menu.setCustomStyleClass('menu-background'); + this.menu.setCustomStyleClass('appmenu-background'); this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); this.menu.connect('menu-animated-closed', () => { this._clearAllSelections(); @@ -1144,12 +1144,12 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._updateIconAndLabel(); this._searchInactiveIcon = new St.Icon({ - style_class: 'menu-search-entry-icon', + style_class: 'appmenu-search-entry-icon', icon_name: 'edit-find', icon_type: St.IconType.SYMBOLIC, }); this._searchActiveIcon = new St.Icon({ - style_class: 'menu-search-entry-icon', + style_class: 'appmenu-search-entry-icon', icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC, }); @@ -1378,7 +1378,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { for (let i = 0; i < n; i++) { this._applicationsButtons[i].actor.show(); } - this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; + this._allAppsCategoryButton.actor.style_class = "appmenu-category-button-selected"; Mainloop.idle_add(() => { if(this.lastSelectedCategory !== null) //if a category is already selected @@ -1528,7 +1528,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { return false; }; - menu.actor.set_style_class_name('menu-context-menu'); + menu.actor.set_style_class_name('appmenu-context-menu'); menu.connect('open-state-changed', this._contextMenuOpenStateChanged.bind(this)); this.contextMenu = menu; this.applicationsBox.add_actor(menu.actor); @@ -1775,7 +1775,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _buttonEnterEvent(button) { this.categoriesBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); - this.applicationsBox.get_children().forEach(child => child.set_style_class_name("menu-application-button")); + this.applicationsBox.get_children().forEach(child => child.set_style_class_name("appmenu-application-button")); this.favoriteAppsBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); this.placesBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); this.systemButtonsBox.get_children().forEach(child => child.remove_style_pseudo_class("hover")); @@ -1787,7 +1787,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { if (button.categoryId !== this.lastSelectedCategory) { if (this.categoryHover) { this.categoriesBox.get_children().forEach(child => - child.set_style_class_name("menu-category-button")); + child.set_style_class_name("appmenu-category-button")); button.activate(); } else { button.actor.add_style_pseudo_class("hover"); @@ -2125,9 +2125,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let button = new SimpleMenuItem(this, { name: _("Clear list"), description: _("Clear all recent documents"), type: 'recent-clear', - styleClass: 'menu-application-button' }); + styleClass: 'appmenu-application-button' }); button.addIcon(22, 'edit-clear', null, true); - button.addLabel(button.name, 'menu-application-button-label'); + button.addLabel(button.name, 'appmenu-application-button-label'); button.label.set_style('font-weight: bold;'); button.activate = () => { this.menu.close(); @@ -2141,10 +2141,10 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.noRecentDocuments = true; let button = new SimpleMenuItem(this, { name: _("No recent documents"), type: 'no-recent', - styleClass: 'menu-application-button', + styleClass: 'appmenu-application-button', reactive: false, activatable: false }); - button.addLabel(button.name, 'menu-application-button-label'); + button.addLabel(button.name, 'appmenu-application-button-label'); this._recentButtons.push(button); this.applicationsBox.add_actor(button.actor); button.actor.visible = this.menu.isOpen && this.lastSelectedCategory === "recent"; @@ -2348,7 +2348,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _buildSidebar() { this.sidebar = new St.BoxLayout({ - style_class: 'menu-sidebar', + style_class: 'appmenu-sidebar', vertical: true }); @@ -2373,7 +2373,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.sidebarAppsBox = new SidebarAppsBox().actor; this.sidebarAppsScrollBox = new St.ScrollView({ y_align: St.Align.START, - style_class: 'menu-sidebar-scrollbox' + style_class: 'appmenu-sidebar-scrollbox' }); this.sidebarAppsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.sidebarAppsScrollBox.add_actor(this.sidebarAppsBox); @@ -2404,7 +2404,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.main_container = new St.BoxLayout({ vertical: false, - style_class: 'menu-main-box' + style_class: 'appmenu-main-box' }); this.main_container._delegate = null; section.addActor(this.main_container, { @@ -2419,11 +2419,11 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.main_container.add(this.right_box, { expand: true }); this.searchBox = new St.BoxLayout({ - style_class: 'menu-search-box', + style_class: 'appmenu-search-box', vertical: false, }); this.searchEntry = new St.Entry({ - name: 'menu-search-entry', + name: 'appmenu-search-entry', track_hover: true, can_focus: true, }); @@ -2449,12 +2449,12 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { }); this.categoriesBox = new St.BoxLayout({ - style_class: 'menu-categories-box', + style_class: 'appmenu-categories-box', vertical: true, accessible_role: Atk.Role.LIST }); - this.categoriesScrollBox = new St.ScrollView({ style_class: 'menu-categories-scrollbox' }); + this.categoriesScrollBox = new St.ScrollView({ style_class: 'appmenu-categories-scrollbox' }); this.categoriesScrollBox.add_actor(this.categoriesBox); this.categoriesScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.categoriesScrollBox.set_clip_to_allocation(true); @@ -2463,10 +2463,10 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesApplicationsBox.actor.add(this.categoriesScrollBox); this.applicationsBox = new St.BoxLayout({ - style_class: 'menu-applications-box', + style_class: 'appmenu-applications-box', vertical:true }); - this.applicationsScrollBox = new St.ScrollView({ style_class: 'vfade menu-applications-scrollbox'}); + this.applicationsScrollBox = new St.ScrollView({ style_class: 'vfade appmenu-applications-scrollbox'}); this.applicationsScrollBox.add_actor(this.applicationsBox); this.applicationsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); this.applicationsScrollBox.set_clip_to_allocation(true); @@ -2490,7 +2490,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this.categoriesApplicationsBox.actor.add(this.applicationsScrollBox, {span: -1, expand: true}); this.systemButtonsBox = new St.BoxLayout({ - style_class: 'menu-system-buttons-box', + style_class: 'appmenu-system-buttons-box', y_expand: false, x_expand: false, x_align: Clutter.ActorAlign.END @@ -2561,9 +2561,9 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let mag_on = this.a11y_settings.get_boolean("screen-magnifier-enabled") && this.a11y_mag_settings.get_double("mag-factor") > 1.0; if (mag_on) { - this.applicationsScrollBox.style_class = "menu-applications-scrollbox"; + this.applicationsScrollBox.style_class = "appmenu-applications-scrollbox"; } else { - this.applicationsScrollBox.style_class = "vfade menu-applications-scrollbox"; + this.applicationsScrollBox.style_class = "vfade appmenu-applications-scrollbox"; } } @@ -2581,11 +2581,11 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { _clearAllSelections() { this.applicationsBox.get_children().forEach(actor => { - actor.style_class = "menu-application-button"; + actor.style_class = "appmenu-application-button"; }); this.categoriesBox.get_children().forEach(actor => { actor.remove_style_pseudo_class("hover") - actor.style_class = "menu-category-button"; + actor.style_class = "appmenu-category-button"; actor.show(); }); this.placesBox.get_children().forEach(actor => { @@ -2720,12 +2720,12 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { let button = categoriesButtons[i]; let icon = button._delegate.icon; if (active){ - button.set_style_class_name("menu-category-button"); + button.set_style_class_name("appmenu-category-button"); if (icon) { icon.set_opacity(255); } } else { - button.set_style_class_name("menu-category-button-greyed"); + button.set_style_class_name("appmenu-category-button-greyed"); if (icon) { let icon_opacity = icon.get_theme_node().get_double('opacity'); icon_opacity = Math.min(Math.max(0, icon_opacity), 1); @@ -2777,7 +2777,7 @@ class CinnamonMenuApplet extends Applet.TextIconApplet { this._previousSearchPattern = ""; this._setCategoriesButtonActive(true); this._select_category(); - this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; + this._allAppsCategoryButton.actor.style_class = "appmenu-category-button-selected"; this._activeContainer = null; } }