diff --git a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java index e20e71289c835ed2a40652decdc7799d165be217..2c211bdfb89fd837854fb8d6f06f232a80e6cf0f 100644 --- a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java +++ b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java @@ -14,6 +14,8 @@ import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.JellyTagException; import org.apache.commons.jelly.Script; import org.apache.commons.jelly.XMLOutput; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; @@ -128,6 +130,18 @@ public interface ModelObjectWithContextMenu extends ModelObject { return this; } + /** + * Add a header row (no icon, no URL, rendered in header style). + * + * @since TODO + */ + @Restricted(DoNotUse.class) // manage.jelly only + public ContextMenu addHeader(String title) { + final MenuItem item = new MenuItem().withDisplayName(title); + item.header = true; + return add(item); + } + /** * Adds a manually constructed {@link MenuItem} * @@ -259,6 +273,13 @@ public interface ModelObjectWithContextMenu extends ModelObject { */ @Exported public boolean requiresConfirmation; + + /** + * True to display this item as a section header. + * @since TODO + */ + @Exported public boolean header; + /** * If this is a submenu, definition of subitems. */ diff --git a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly index 4c815b62984f1474f061a46582a661619c1cdac2..b5b8f72c3ae36952ef6f1df4f71b0f7b6860e4cd 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly @@ -49,7 +49,7 @@ THE SOFTWARE.

${category.key.label}

- + ${taskTags!=null and attrs.contextMenu!='false' ? taskTags.addHeader(category.key.label) : null}
diff --git a/core/src/main/resources/lib/layout/breadcrumbs.css b/core/src/main/resources/lib/layout/breadcrumbs.css index 38e5a85a2f04fa725ed9cb20018430d090baa5bc..b305e1c7c68aaca41b4e95b124f1d2627aaba85c 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.css +++ b/core/src/main/resources/lib/layout/breadcrumbs.css @@ -126,3 +126,8 @@ A.model-link.inside, #breadcrumbs A.inside {/* additional 'inside' class allows pre-allocation of the context menu space */ padding-right: 16px; } + +#breadcrumb-menu .header { + font-weight: bold; + font-size: 0.875rem; +} diff --git a/core/src/main/resources/lib/layout/breadcrumbs.js b/core/src/main/resources/lib/layout/breadcrumbs.js index 7da47e1d4fbf83da2ec8c0c058067ef33ea279ea..de91518b11e22f78c95d486ce133ae06db3830c5 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.js +++ b/core/src/main/resources/lib/layout/breadcrumbs.js @@ -203,7 +203,11 @@ var breadcrumbs = (function() { onComplete:function (x) { var a = x.responseText.evalJSON().items; function fillMenuItem(e) { - e.text = makeMenuHtml(e.icon, e.displayName); + if (e.header) { + e.text = makeMenuHtml(e.icon, "" + e.displayName + ""); + } else { + e.text = makeMenuHtml(e.icon, e.displayName); + } if (e.subMenu!=null) e.subMenu = {id:"submenu"+(iota++), itemdata:e.subMenu.items.each(fillMenuItem)}; if (e.requiresConfirmation) {