This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3889-icon.overhaul
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 01ce2059d844cd7e97a2f1d8ad3fb8c2cb684fef
Author: Andi Huber <[email protected]>
AuthorDate: Thu Aug 28 14:52:22 2025 +0200

    CAUSEWAY-3889: unified icon support method
    
    unifies iconName, iconData and faIconLayers
---
 .../causeway/applib/annotation/ObjectSupport.java  | 30 ++++++++
 .../services/appfeatui/ApplicationFeatureMenu.java |  6 +-
 .../appfeatui/ApplicationFeatureViewModel.java     |  4 +-
 .../applib/services/title/TitleService.java        | 11 ++-
 .../progmodel/ProgrammingModelConstants.java       | 15 ++--
 .../metamodel/facets/FacetFactoryAbstract.java     |  2 +
 .../core/metamodel/facets/ImperativeAspect.java    | 19 +++++
 ...mainObjectLayoutAnnotationUsingIconUiEvent.java | 88 ++++++++++++----------
 .../metamodel/facets/object/icon/IconFacet.java    |  8 +-
 .../metamodel/facets/object/icon/ObjectIcon.java   |  6 +-
 .../facets/object/icon/ObjectIconEmbedded.java     |  3 +-
 .../metamodel/facets/object/icon/ObjectIconFa.java |  3 +-
 .../facets/object/icon/ObjectIconService.java      | 31 +++-----
 .../facets/object/icon/ObjectIconUrlBased.java     |  3 +-
 ...NameMethod.java => IconFacetViaIconMethod.java} | 45 ++++++-----
 .../method/FaFacetViaIconFaLayersMethod.java       | 80 --------------------
 .../ident/IconFacetFromProjectionFacet.java        | 33 ++++----
 .../object/support/ObjectSupportFacetFactory.java  | 67 ++++++++--------
 .../facets/object/title/TitleRenderRequest.java    |  4 +-
 .../inspect/model/MetamodelInspectView.java        | 21 +++---
 .../core/metamodel/object/ManagedObject.java       | 15 +---
 .../services/title/TitleServiceDefault.java        | 27 +++----
 .../core/metamodel/spec/ObjectSpecification.java   | 32 +++-----
 .../spec/impl/ObjectSpecificationDefault.java      | 46 ++++-------
 .../metamodel/facets/FacetFactoryTestAbstract.java |  4 +-
 .../IconFacetMethodFaTest.java}                    | 35 +++++----
 .../ident/icon/IconFacetMethodFactoryTest.java     |  9 ++-
 .../object/ident/icon/IconFacetMethodTest.java     | 23 +++---
 .../FontAwesomeLayersFacetMethodFactoryTest.java   | 40 ----------
 .../ObjectSupportFacetFactoryTestAbstract.java     |  5 +-
 .../services/title/TitleServiceDefaultTest.java    |  4 +-
 .../mmtestsupport/MetaModelContext_forTesting.java |  3 +-
 .../mmtestsupport/TitleServiceForTesting.java}     | 27 +++----
 .../icons/ObjectIconServiceDefault.java            | 82 +++++++++++---------
 .../testdomain/model/good/ProperFullyAbstract.java |  2 +-
 .../testdomain/model/good/ProperFullyImpl.java     |  5 +-
 .../good/ProperMemberInheritanceAbstract.java      |  4 +-
 .../good/ProperMemberInheritanceInterface.java     |  5 +-
 .../interaction/DomainObjectTesterFactory.java     | 11 ++-
 .../DomainModelTest_usingGoodDomain.java           | 10 ++-
 .../MetaModelRegressionTest.verify.approved.xml    | 24 +++---
 .../viewer/commons/model/mixin/HasIcon.java        |  6 +-
 .../viewer/controller/ResourceController.java      |  3 +-
 .../resources/DomainObjectResourceServerside.java  |  3 +-
 .../wicket/model/models/BookmarkTreeNode.java      |  2 +
 .../wicket/model/models/BookmarkableModel.java     |  3 +-
 .../viewer/wicket/model/models/UiObjectWkt.java    |  7 +-
 .../wicket/ui/components/layout/bs/col/Col.java    |  7 +-
 .../object/icontitle/ObjectIconAndTitlePanel.java  | 22 ++++--
 .../icontitle/ObjectIconAndTitlePanelFactory.java  |  6 +-
 .../icontitle/ObjectIconTitleAndCopyLinkPanel.java |  7 +-
 .../ObjectIconTitleAndCopyLinkPanelFactory.java    |  3 +-
 52 files changed, 449 insertions(+), 512 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/ObjectSupport.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/ObjectSupport.java
index 63bac495379..1e9dca58b9e 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/ObjectSupport.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/ObjectSupport.java
@@ -24,6 +24,9 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.causeway.applib.fa.FontAwesomeLayers;
+import org.apache.causeway.commons.net.DataUri;
+
 /**
  * Indicates that a method is a supporting-method that provides UI hints 
(title, iconName, layout, cssClass)
  * to its <i>Object</i>.
@@ -47,4 +50,31 @@
 @Domain.Include // meta annotation, in support of meta-model validation
 public @interface ObjectSupport {
 
+    /**
+     * Rendering context for object icons.
+     * An object's icon is either rendered within the object detail page 
header (along its title)
+     * or within a table row.
+     */
+    public enum IconWhere {
+        OBJECT_HEADER,
+        TABLE_ROW //TODO also TREE_NODE and SELECT_DROPDOWN
+    }
+
+    public sealed interface IconResource
+    permits ClassPathIconResource, FontAwesomeIconResource, 
EmbeddedIconResource {
+
+    }
+
+    public record ClassPathIconResource(
+        String suffix) implements IconResource {
+    }
+
+    public record FontAwesomeIconResource(
+        FontAwesomeLayers faLayers) implements IconResource {
+    }
+
+    public record EmbeddedIconResource(
+        DataUri dataUri) implements IconResource {
+    }
+
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureMenu.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureMenu.java
index 3cb99ddea4c..96f3bdc94a3 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureMenu.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureMenu.java
@@ -63,10 +63,10 @@ public static abstract class CollectionDomainEvent<T>
     public static abstract class ActionDomainEvent
     extends CausewayModuleApplib.ActionDomainEvent<ApplicationFeatureMenu> {}
 
-    // -- ICON NAME
+    // -- ICON
 
-    @ObjectSupport public String iconName() {
-        return "applicationFeature";
+    @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+        return new ObjectSupport.ClassPathIconResource("applicationFeature");
     }
 
     // -- ALL PACKAGES
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureViewModel.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureViewModel.java
index 49e84685122..c36588944cd 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureViewModel.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/appfeatui/ApplicationFeatureViewModel.java
@@ -143,8 +143,8 @@ public ApplicationFeatureViewModel() {
     @ObjectSupport public String title() {
         return getFullyQualifiedName();
     }
-    @ObjectSupport public String iconName() {
-        return "applicationFeature";
+    @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+        return new ObjectSupport.ClassPathIconResource("applicationFeature");
     }
 
     // -- VIEWMODEL CONTRACT
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/title/TitleService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/title/TitleService.java
index 37c44807090..1080d070e33 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/title/TitleService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/title/TitleService.java
@@ -18,11 +18,14 @@
  */
 package org.apache.causeway.applib.services.title;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconResource;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
+
 /**
  * Provides methods to programmatically obtain the title and icon of a domain
  * object.
  *
- * @since 1.x {@index}
+ * @since 1.x {@index} revised for 4.0
  */
 public interface TitleService {
 
@@ -30,12 +33,12 @@ public interface TitleService {
      * Returns the title of the object (as rendered in the UI by the
      * framework's viewers).
      */
-    String titleOf(final Object domainObject);
+    String titleOf(Object domainObject);
 
     /**
-     * Returns the icon name of the object (as rendered in the UI by the
+     * Returns the icon of the object (as rendered in the UI by the
      * framework's viewers).
      */
-    String iconNameOf(final Object domainObject);
+    IconResource iconOf(Object domainObject, IconWhere iconWhere);
 
 }
diff --git 
a/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java
 
b/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java
index 54da4c83252..448eae011fa 100644
--- 
a/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java
+++ 
b/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java
@@ -35,8 +35,10 @@
 import java.util.function.IntFunction;
 import java.util.stream.Stream;
 
-import org.springframework.context.annotation.Configuration;
+import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
+
+import org.springframework.context.annotation.Configuration;
 import org.springframework.stereotype.Repository;
 
 import org.apache.causeway.applib.Identifier;
@@ -44,7 +46,7 @@
 import org.apache.causeway.applib.annotation.MemberSupport;
 import org.apache.causeway.applib.annotation.ObjectLifecycle;
 import org.apache.causeway.applib.annotation.ObjectSupport;
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconResource;
 import org.apache.causeway.applib.services.i18n.TranslatableString;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.functional.Try;
@@ -65,7 +67,6 @@
 import static 
org.apache.causeway.commons.internal.reflection._Reflect.predicates.paramCount;
 
 import lombok.Getter;
-import org.jspecify.annotations.NonNull;
 import lombok.RequiredArgsConstructor;
 
 public final class ProgrammingModelConstants {
@@ -249,7 +250,8 @@ public enum ReturnTypeCategory {
         BOOLEAN(boolean.class),
         STRING(String.class),
         TRANSLATABLE(String.class, TranslatableString.class),
-        FONTAWESOME_LAYERS(FontAwesomeLayers.class);
+        ICON_RESOURCE(IconResource.class),
+        ;
         ReturnTypeCategory(final Class<?> ...returnTypes) {
             this.returnTypes = Can.of(returnTypes);
         }
@@ -292,8 +294,9 @@ public enum ObjectSupportMethod {
 
         TITLE(ReturnTypeCategory.TRANSLATABLE, "title"),
         CSS_CLASS(ReturnTypeCategory.STRING, "cssClass"),
-        ICON_NAME(ReturnTypeCategory.STRING, "iconName"),
-        ICON_FA_LAYERS(ReturnTypeCategory.FONTAWESOME_LAYERS, "iconFaLayers"),
+
+        ICON(ReturnTypeCategory.ICON_RESOURCE, "icon"),
+
         LAYOUT(ReturnTypeCategory.STRING, "layout"),
 
         /** as a fallback in the absence of other title providers */
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/FacetFactoryAbstract.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/FacetFactoryAbstract.java
index 00536c04b48..de808242092 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/FacetFactoryAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/FacetFactoryAbstract.java
@@ -22,6 +22,7 @@
 
 import org.jspecify.annotations.Nullable;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.commons.collections.ImmutableEnumSet;
 import org.apache.causeway.commons.internal.reflection._ClassCache;
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
@@ -87,5 +88,6 @@ public <F extends Facet> Optional<F> addFacetIfPresent(final 
@Nullable Optional<
 
     protected static final Class<?>[] NO_ARG = new Class<?>[0];
     protected static final Class<?>[] STRING_ARG = new Class<?>[] 
{String.class};
+    protected static final Class<?>[] ICON_WHERE_ARG = new Class<?>[] 
{ObjectSupport.IconWhere.class};
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/ImperativeAspect.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/ImperativeAspect.java
index 20980c854f7..fa2a51bf083 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/ImperativeAspect.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/ImperativeAspect.java
@@ -54,6 +54,12 @@ public Object invokeSingleMethod(final ManagedObject 
domainObject) {
         return returnValue;
     }
 
+    public Object invokeSingleMethod(final ManagedObject domainObject, final 
Object arg0) {
+        var method = methods.getFirstElseFail().asMethodElseFail(); // 
expected regular, as the factories only creates regular
+        final Object returnValue = 
MmInvokeUtils.invokeWithSingleArgPojo(method.method(), domainObject, arg0);
+        return returnValue;
+    }
+
     public <T> T eval(
             final ManagedObject domainObject,
             final T fallback) {
@@ -66,4 +72,17 @@ public <T> T eval(
         }
     }
 
+    public <T> T eval(
+            final ManagedObject domainObject,
+            final T fallback,
+            final Object arg0) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(domainObject)) return 
fallback;
+
+        try {
+            return _Casts.uncheckedCast(invokeSingleMethod(domainObject));
+        } catch (final RuntimeException ex) {
+            return fallback;
+        }
+    }
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
index 4423e7a7e18..ef1fd2c0da4 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent.java
@@ -21,21 +21,27 @@
 import java.util.Optional;
 import java.util.function.BiConsumer;
 
+import org.springframework.util.ClassUtils;
+
 import org.apache.causeway.applib.annotation.DomainObjectLayout;
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.applib.events.EventObjectBase;
 import org.apache.causeway.applib.events.ui.IconUiEvent;
 import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
-import org.apache.causeway.core.metamodel.facets.object.icon.IconFacetAbstract;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.object.MmEventUtils;
 import 
org.apache.causeway.core.metamodel.services.events.MetamodelEventService;
 
-public class IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent
-extends IconFacetAbstract {
+public record IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent(
+    Class<? extends IconUiEvent<Object>> iconUiEventClass,
+    MetamodelEventService metamodelEventService,
+    FacetHolder facetHolder)
+implements IconFacet {
 
     public static 
Optional<IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent> create(
             final Optional<DomainObjectLayout> domainObjectLayoutIfAny,
@@ -43,67 +49,69 @@ public static 
Optional<IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent>
             final FacetHolder facetHolder) {
 
         return domainObjectLayoutIfAny
-                .map(DomainObjectLayout::iconUiEvent)
-                .filter(iconUiEvent -> MmEventUtils.eventTypeIsPostable(
-                        iconUiEvent,
-                        IconUiEvent.Noop.class,
-                        IconUiEvent.Default.class,
-                        facetHolder.getConfiguration().applib().annotation()
-                            
.domainObjectLayout().iconUiEvent().postForDefault()))
-                .map(iconUiEvent -> {
-                    return new 
IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent(
-                            iconUiEvent, metamodelEventService, facetHolder);
-                });
+            .map(DomainObjectLayout::iconUiEvent)
+            .filter(iconUiEvent -> MmEventUtils.eventTypeIsPostable(
+                    iconUiEvent,
+                    IconUiEvent.Noop.class,
+                    IconUiEvent.Default.class,
+                    facetHolder.getConfiguration().applib().annotation()
+                        .domainObjectLayout().iconUiEvent().postForDefault()))
+            .map(iconUiEvent -> {
+                return new 
IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent(
+                    _Casts.uncheckedCast(iconUiEvent), metamodelEventService, 
facetHolder);
+            });
     }
 
-    private final Class<? extends IconUiEvent<Object>> iconUiEventClass;
-    private final MetamodelEventService metamodelEventService;
+    @Override public FacetHolder getFacetHolder() { return facetHolder; }
+    @Override public Class<? extends Facet> facetType() { return 
IconFacet.class; }
+    @Override public Precedence getPrecedence() { return Precedence.EVENT; }
 
-    public IconFacetViaDomainObjectLayoutAnnotationUsingIconUiEvent(
-            final Class<? extends IconUiEvent<?>> iconUiEventClass,
-                    final MetamodelEventService metamodelEventService,
-                    final FacetHolder holder) {
-        super(holder, Precedence.EVENT);
-        this.iconUiEventClass = _Casts.uncheckedCast(iconUiEventClass);
-        this.metamodelEventService = metamodelEventService;
-    }
 
     @Override
-    public Optional<String> iconName(final ManagedObject owningAdapter) {
+    public Optional<ObjectSupport.IconResource> icon(ManagedObject 
domainObject, ObjectSupport.IconWhere iconWhere) {
+        return iconName(domainObject, iconWhere)
+            .map(ObjectSupport.ClassPathIconResource::new);
+    }
 
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(owningAdapter)) {
-            return Optional.empty();
-        }
+    private Optional<String> iconName(final ManagedObject domainObject, 
ObjectSupport.IconWhere iconWhere) {
 
-        final IconUiEvent<Object> iconUiEvent = newIconUiEvent(owningAdapter);
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(domainObject)) return 
Optional.empty();
+
+        final IconUiEvent<Object> iconUiEvent = newIconUiEvent(domainObject);
 
         metamodelEventService.fireIconUiEvent(iconUiEvent);
 
-        final String iconName = iconUiEvent.getIconName();
+        var iconName = iconUiEvent.getIconName();
 
         if(iconName == null) {
             // ie no subscribers out there...
 
-            final IconFacet underlyingIconFacet = getSharedFacetRanking()
-            
.flatMap(facetRanking->facetRanking.getWinnerNonEvent(IconFacet.class))
-            .orElse(null);
+            var icon = underlyingIconFacet()
+                
.flatMap(underlyingIconFacet->underlyingIconFacet.icon(domainObject, iconWhere))
+                .orElse(null);
 
-            if(underlyingIconFacet!=null) {
-                return underlyingIconFacet.iconName(owningAdapter);
-            }
+            if(icon instanceof ObjectSupport.ClassPathIconResource 
cpIconResource)
+                iconName = cpIconResource.suffix();
         }
 
         return _Strings.nonEmpty(iconName);
     }
 
+    @Override
+    public void visitAttributes(final BiConsumer<String, Object> visitor) {
+        visitor.accept("facet", ClassUtils.getShortName(getClass()));
+        visitor.accept("precedence", getPrecedence().name());
+        visitor.accept("iconUiEventClass", iconUiEventClass);
+    }
+
     private IconUiEvent<Object> newIconUiEvent(final ManagedObject 
owningAdapter) {
         return EventObjectBase.getInstanceWithSourceSupplier(iconUiEventClass, 
owningAdapter::getPojo).orElseThrow();
     }
 
-    @Override
-    public void visitAttributes(final BiConsumer<String, Object> visitor) {
-        super.visitAttributes(visitor);
-        visitor.accept("iconUiEventClass", iconUiEventClass);
+    private Optional<IconFacet> underlyingIconFacet() {
+        return getSharedFacetRanking()
+            
.flatMap(facetRanking->facetRanking.getWinnerNonEvent(IconFacet.class));
     }
 
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacet.java
index 6380c5d450e..a34cfae096a 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacet.java
@@ -20,6 +20,8 @@
 
 import java.util.Optional;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
@@ -35,11 +37,13 @@
  * pending approval, approved, rejected).
  *
  * <p>
- * In the standard Apache Causeway Programming Model, typically corresponds to 
a method named {@code iconName}.
+ * In the Apache Causeway Programming Model, corresponds to either a method 
named {@code iconName}
+ * or {@code iconData}.
  *
  * @see TitleFacet
  */
 public interface IconFacet extends Facet {
 
-    public Optional<String> iconName(final ManagedObject object);
+    Optional<ObjectSupport.IconResource> icon(ManagedObject object, IconWhere 
iconWhere);
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
index 4a5bbcf321b..4bcb890d579 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
@@ -20,11 +20,13 @@
 
 import java.io.Serializable;
 
+import org.apache.causeway.applib.services.title.TitleService;
+
 /**
  * Icon image data class-path resource reference.
  *
- * @see ObjectIconService
- * @since 2.0 revised for 4.0
+ * @see TitleService
+ * @since 2.0 revised for 4.0 {@index}
  */
 public sealed interface ObjectIcon extends Serializable
 permits ObjectIconFa, ObjectIconEmbedded, ObjectIconUrlBased {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
index 8fb64f77914..70cc2c10f41 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
@@ -18,13 +18,14 @@
  */
 package org.apache.causeway.core.metamodel.facets.object.icon;
 
+import org.apache.causeway.applib.services.title.TitleService;
 import org.apache.causeway.commons.net.DataUri;
 
 /**
  * Icon image based on {@link DataUri}
  *
  * @see ObjectIcon
- * @see ObjectIconService
+ * @see TitleService
  * @since 4.0
  */
 public record ObjectIconEmbedded(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconFa.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconFa.java
index 335a4673767..64154b5682c 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconFa.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconFa.java
@@ -21,12 +21,13 @@
 import java.nio.charset.StandardCharsets;
 
 import org.apache.causeway.applib.fa.FontAwesomeLayers;
+import org.apache.causeway.applib.services.title.TitleService;
 
 /**
  * Icon image based on {@link FontAwesomeLayers}
  *
  * @see ObjectIcon
- * @see ObjectIconService
+ * @see TitleService
  * @since 4.0
  */
 public record ObjectIconFa(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconService.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconService.java
index 8c2d6bb5cf9..1c1a00f95a3 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconService.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconService.java
@@ -18,35 +18,26 @@
  */
 package org.apache.causeway.core.metamodel.facets.object.icon;
 
-import java.util.Optional;
-
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconResource;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 
 /**
- * Creates {@link ObjectIcon}(s), which are class-path resource references.
- * <p>
- * Clients should not use this service directly. Either use
- * {@link ManagedObject#getIcon()} or
- * {@link 
ObjectSpecification#getIcon(org.apache.causeway.core.metamodel.object.ManagedObject)}.
+ * Resolves instances of {@link IconResource} to instances of {@link 
ObjectIcon}.
+ *
+ * <p>Clients should not use this service directly. Either use
+ * {@link ManagedObject#getIcon(IconWhere)} or
+ * {@link 
ObjectSpecification#getIcon(org.apache.causeway.core.metamodel.object.ManagedObject,
 IconWhere)}.
  *
  * @apiNote internal service, used by the metamodel
  *
- * @see ManagedObject#getIcon()
- * @see 
ObjectSpecification#getIcon(org.apache.causeway.core.metamodel.object.ManagedObject)
- * @since 2.0
+ * @see ManagedObject#getIcon(IconWhere)
+ * @see 
ObjectSpecification#getIcon(org.apache.causeway.core.metamodel.object.ManagedObject,
 IconWhere)
+ * @since 2.0 revised for 4.0
  */
 public interface ObjectIconService {
 
-    /**
-     * {@link ObjectIcon} for given {@link ObjectSpecification}
-     * and iconNameSuffix or font-awesome layers.
-     * @return non-null
-     */
-    ObjectIcon getObjectIcon(
-            ObjectSpecification specification,
-            Optional<String> iconNameSuffix,
-            Optional<FontAwesomeLayers> faLayers);
+    ObjectIcon getObjectIcon(ManagedObject managedObject, IconWhere iconWhere);
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
index 0e094b69b73..74c3eb6324c 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
@@ -22,6 +22,7 @@
 import java.net.URL;
 import java.util.Objects;
 
+import org.apache.causeway.applib.services.title.TitleService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.internal._Constants;
 import org.apache.causeway.commons.internal.base._Bytes;
@@ -32,7 +33,7 @@
  * Icon image data class-path resource reference.
  *
  * @see ObjectIcon
- * @see ObjectIconService
+ * @see TitleService
  * @since 4.0
  */
 public record ObjectIconUrlBased(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconMethod.java
similarity index 59%
rename from 
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
rename to 
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconMethod.java
index 1bfc6e131be..7060dea4071 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/method/IconFacetViaIconMethod.java
@@ -23,50 +23,47 @@
 
 import org.jspecify.annotations.Nullable;
 
-import org.apache.causeway.commons.internal.base._Strings;
+import org.springframework.util.ClassUtils;
+
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import 
org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod;
+import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.HasImperativeAspect;
 import org.apache.causeway.core.metamodel.facets.ImperativeAspect;
 import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
-import org.apache.causeway.core.metamodel.facets.object.icon.IconFacetAbstract;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 
-import lombok.Getter;
-import org.jspecify.annotations.NonNull;
-
-public class IconFacetViaIconNameMethod
-extends IconFacetAbstract
-implements HasImperativeAspect {
-
-    @Getter(onMethod_ = {@Override}) private final @NonNull ImperativeAspect 
imperativeAspect;
+public record IconFacetViaIconMethod(
+    ImperativeAspect imperativeAspect,
+    FacetHolder facetHolder)
+implements IconFacet, HasImperativeAspect {
 
     public static Optional<IconFacet> create(
             final @Nullable ResolvedMethod methodIfAny,
             final FacetHolder holder) {
-
         return Optional.ofNullable(methodIfAny)
-        .map(method->
-            new IconFacetViaIconNameMethod(
-                    ImperativeAspect.singleRegularMethod(method, 
Intent.UI_HINT),
-                    holder));
+            .map(method->
+                new IconFacetViaIconMethod(
+                        ImperativeAspect.singleRegularMethod(method, 
Intent.UI_HINT),
+                        holder));
     }
 
-    private IconFacetViaIconNameMethod(
-            final ImperativeAspect imperativeAspect,
-            final FacetHolder holder) {
-        super(holder);
-        this.imperativeAspect = imperativeAspect;
-    }
+    @Override public FacetHolder getFacetHolder() { return facetHolder; }
+    @Override public Class<? extends Facet> facetType() { return 
IconFacet.class; }
+    @Override public Precedence getPrecedence() { return Precedence.DEFAULT; }
+    @Override public ImperativeAspect getImperativeAspect() { return 
imperativeAspect; }
 
     @Override
-    public Optional<String> iconName(final ManagedObject domainObject) {
-        return _Strings.nonEmpty(imperativeAspect.eval(domainObject, 
(String)null));
+    public Optional<ObjectSupport.IconResource> icon(ManagedObject 
domainObject, IconWhere iconWhere) {
+        return Optional.ofNullable(imperativeAspect.eval(domainObject, 
(ObjectSupport.IconResource)null, iconWhere));
     }
 
     @Override
     public void visitAttributes(final BiConsumer<String, Object> visitor) {
-        super.visitAttributes(visitor);
+        visitor.accept("facet", ClassUtils.getShortName(getClass()));
+        visitor.accept("precedence", getPrecedence().name());
         imperativeAspect.visitAttributes(visitor);
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/iconfa/method/FaFacetViaIconFaLayersMethod.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/iconfa/method/FaFacetViaIconFaLayersMethod.java
deleted file mode 100644
index 3d04ffeacbf..00000000000
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/iconfa/method/FaFacetViaIconFaLayersMethod.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.core.metamodel.facets.object.iconfa.method;
-
-import java.util.Optional;
-import java.util.function.BiConsumer;
-
-import org.jspecify.annotations.Nullable;
-
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
-import 
org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod;
-import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
-import org.apache.causeway.core.metamodel.facets.HasImperativeAspect;
-import org.apache.causeway.core.metamodel.facets.ImperativeAspect;
-import org.apache.causeway.core.metamodel.facets.members.iconfa.FaFacet;
-import 
org.apache.causeway.core.metamodel.facets.members.iconfa.FaImperativeFacetAbstract;
-import 
org.apache.causeway.core.metamodel.facets.members.iconfa.FaLayersProvider;
-import org.apache.causeway.core.metamodel.object.ManagedObject;
-
-import lombok.Getter;
-import org.jspecify.annotations.NonNull;
-
-public class FaFacetViaIconFaLayersMethod
-extends FaImperativeFacetAbstract
-implements HasImperativeAspect {
-
-    @Getter(onMethod_ = {@Override}) private final @NonNull ImperativeAspect 
imperativeAspect;
-
-    public static Optional<FaFacet> create(
-            final @Nullable ResolvedMethod methodIfAny,
-            final FacetHolder holder) {
-
-        return Optional.ofNullable(methodIfAny)
-        .map(method->
-            new FaFacetViaIconFaLayersMethod(
-                    ImperativeAspect.singleRegularMethod(method, 
Intent.UI_HINT),
-                    holder));
-    }
-
-    private FaFacetViaIconFaLayersMethod(
-            final ImperativeAspect imperativeAspect,
-            final FacetHolder holder) {
-        super(holder, Precedence.IMPERATIVE);
-        this.imperativeAspect = imperativeAspect;
-    }
-
-    @Override
-    public FaLayersProvider getFaLayersProvider(final ManagedObject 
domainObject) {
-        return () -> evalLayers(domainObject);
-    }
-
-    @Override
-    public void visitAttributes(final BiConsumer<String, Object> visitor) {
-        super.visitAttributes(visitor);
-        imperativeAspect.visitAttributes(visitor);
-    }
-
-    // -- HELPER
-
-    FontAwesomeLayers evalLayers(final ManagedObject domainObject) {
-        return imperativeAspect.eval(domainObject, (FontAwesomeLayers)null);
-    }
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/projection/ident/IconFacetFromProjectionFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/projection/ident/IconFacetFromProjectionFacet.java
index 691e31504e1..0ff1196bb5d 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/projection/ident/IconFacetFromProjectionFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/projection/ident/IconFacetFromProjectionFacet.java
@@ -21,35 +21,36 @@
 import java.util.Optional;
 import java.util.function.BiConsumer;
 
+import org.jspecify.annotations.NonNull;
+
+import org.springframework.util.ClassUtils;
+
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
-import org.apache.causeway.core.metamodel.facets.object.icon.IconFacetAbstract;
+import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.projection.ProjectionFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 
-import org.jspecify.annotations.NonNull;
+public record IconFacetFromProjectionFacet(
+    ProjectionFacet projectionFacet,
+    FacetHolder facetHolder)
+implements IconFacet {
 
-public class IconFacetFromProjectionFacet
-extends IconFacetAbstract {
-
-    private final @NonNull ProjectionFacet projectionFacet;
-
-    public IconFacetFromProjectionFacet(
-            final ProjectionFacet projectionFacet,
-            final FacetHolder holder) {
-        super(holder);
-        this.projectionFacet = projectionFacet;
-    }
+    @Override public FacetHolder getFacetHolder() { return facetHolder; }
+    @Override public Class<? extends Facet> facetType() { return 
IconFacet.class; }
+    @Override public Precedence getPrecedence() { return Precedence.DEFAULT; }
 
     @Override
-    public Optional<String> iconName(final ManagedObject targetAdapter) {
+    public Optional<ObjectSupport.IconResource> icon(final ManagedObject 
targetAdapter, final ObjectSupport.IconWhere iconWhere) {
         var projectedAdapter = projectionFacet.projected(targetAdapter);
-        return projectedAdapter.objSpec().getIconName(projectedAdapter);
+        return projectedAdapter.objSpec().getIcon(projectedAdapter, iconWhere);
     }
 
     @Override
     public void visitAttributes(final BiConsumer<String, Object> visitor) {
-        super.visitAttributes(visitor);
+        visitor.accept("facet", ClassUtils.getShortName(getClass()));
+        visitor.accept("precedence", getPrecedence().name());
         visitor.accept("projectionFacet", 
projectionFacet.getClass().getName());
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactory.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactory.java
index 1e96b4b8641..95872194dfb 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactory.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactory.java
@@ -39,8 +39,7 @@
 import 
org.apache.causeway.core.metamodel.facets.object.disabled.method.DisabledObjectFacetViaMethod;
 import 
org.apache.causeway.core.metamodel.facets.object.hidden.HiddenObjectFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.hidden.method.HiddenObjectFacetViaMethod;
-import 
org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod;
-import 
org.apache.causeway.core.metamodel.facets.object.iconfa.method.FaFacetViaIconFaLayersMethod;
+import 
org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod;
 import 
org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacetViaMethod;
 import 
org.apache.causeway.core.metamodel.facets.object.title.methods.TitleFacetFromToStringMethod;
 import 
org.apache.causeway.core.metamodel.facets.object.title.methods.TitleFacetViaTitleMethod;
@@ -70,9 +69,9 @@ public class ObjectSupportFacetFactory
     public ObjectSupportFacetFactory(final MetaModelContext mmc) {
         super(mmc, FeatureType.EVERYTHING_BUT_PARAMETERS, 
OrphanValidation.VALIDATE,
                 Stream.of(ObjectSupportMethod.values())
-                .map(ObjectSupportMethod::getMethodNames)
-                .flatMap(Can::stream)
-                .collect(Can.toCan()));
+                    .map(ObjectSupportMethod::getMethodNames)
+                    .flatMap(Can::stream)
+                    .collect(Can.toCan()));
     }
 
     @Override
@@ -81,13 +80,12 @@ public final void process(final ProcessClassContext 
processClassContext) {
         // priming 'toString()' into Precedence.INFERRED rank
         inferTitleFromToString(processClassContext);
 
-        processObjectSupport(processClassContext, ObjectSupportMethod.HIDDEN, 
HiddenObjectFacetViaMethod::create);
-        processObjectSupport(processClassContext, 
ObjectSupportMethod.DISABLED, DisabledObjectFacetViaMethod::create);
-        processObjectSupport(processClassContext, ObjectSupportMethod.TITLE, 
TitleFacetViaTitleMethod::create);
-        processObjectSupport(processClassContext, ObjectSupportMethod.LAYOUT, 
LayoutPrefixFacetViaMethod::create);
-        processObjectSupport(processClassContext, 
ObjectSupportMethod.ICON_NAME, IconFacetViaIconNameMethod::create);
-        processObjectSupport(processClassContext, 
ObjectSupportMethod.ICON_FA_LAYERS, FaFacetViaIconFaLayersMethod::create);
-        processObjectSupport(processClassContext, 
ObjectSupportMethod.CSS_CLASS, CssClassFacetViaCssClassMethod::create);
+        processObjectSupport(processClassContext, ObjectSupportMethod.HIDDEN, 
NO_ARG, HiddenObjectFacetViaMethod::create);
+        processObjectSupport(processClassContext, 
ObjectSupportMethod.DISABLED, NO_ARG, DisabledObjectFacetViaMethod::create);
+        processObjectSupport(processClassContext, ObjectSupportMethod.TITLE, 
NO_ARG, TitleFacetViaTitleMethod::create);
+        processObjectSupport(processClassContext, ObjectSupportMethod.LAYOUT, 
NO_ARG, LayoutPrefixFacetViaMethod::create);
+        processObjectSupport(processClassContext, ObjectSupportMethod.ICON, 
ICON_WHERE_ARG, IconFacetViaIconMethod::create);
+        processObjectSupport(processClassContext, 
ObjectSupportMethod.CSS_CLASS, NO_ARG, CssClassFacetViaCssClassMethod::create);
     }
 
     @Override
@@ -112,35 +110,36 @@ private void inferTitleFromToString(final 
ProcessClassContext processClassContex
         var toString = ObjectSupportMethod.TO_STRING;
 
         MethodFinder
-        .publicOnly(
-                processClassContext.getCls(),
-                toString.getMethodNames())
-        .withReturnTypeAnyOf(toString.getReturnTypeCategory().getReturnTypes())
-        .streamMethodsMatchingSignature(NO_ARG)
-        .peek(processClassContext::removeMethod)
-        .forEach(method->{
-            addFacetIfPresent(TitleFacetFromToStringMethod
-                    .create(method, processClassContext.getFacetHolder()));
-        });
+            .publicOnly(
+                    processClassContext.getCls(),
+                    toString.getMethodNames())
+            
.withReturnTypeAnyOf(toString.getReturnTypeCategory().getReturnTypes())
+            .streamMethodsMatchingSignature(NO_ARG)
+            .peek(processClassContext::removeMethod)
+            .forEach(method->{
+                addFacetIfPresent(TitleFacetFromToStringMethod
+                        .create(method, processClassContext.getFacetHolder()));
+            });
     }
 
     private void processObjectSupport(
             final ProcessClassContext processClassContext,
             final ObjectSupportMethod objectSupportMethodEnum,
+            final Class<?>[] methodSignature,
             final BiFunction<ResolvedMethod, FacetHolder, Optional<? extends 
Facet>> objectSupportFacetConstructor) {
-
         MethodFinder
-        .objectSupport(
-                processClassContext.getCls(),
-                objectSupportMethodEnum.getMethodNames(),
-                processClassContext.getIntrospectionPolicy())
-        
.withReturnTypeAnyOf(objectSupportMethodEnum.getReturnTypeCategory().getReturnTypes())
-        .streamMethodsMatchingSignature(NO_ARG)
-        .peek(processClassContext::removeMethod)
-        .forEach(method->{
-            addFacetIfPresent(objectSupportFacetConstructor
-                    .apply(method, processClassContext.getFacetHolder()));
-        });
+            .objectSupport(
+                    processClassContext.getCls(),
+                    objectSupportMethodEnum.getMethodNames(),
+                    processClassContext.getIntrospectionPolicy())
+            
.withReturnTypeAnyOf(objectSupportMethodEnum.getReturnTypeCategory().getReturnTypes())
+            .streamMethodsMatchingSignature(methodSignature)
+            .peek(processClassContext::removeMethod)
+            .forEach(method->{
+                addFacetIfPresent(objectSupportFacetConstructor
+                        .apply(method, processClassContext.getFacetHolder()))
+                .orElse(null);
+            });
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/title/TitleRenderRequest.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/title/TitleRenderRequest.java
index 02c9e1bffe8..c255fc13949 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/title/TitleRenderRequest.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/title/TitleRenderRequest.java
@@ -29,8 +29,8 @@ public record TitleRenderRequest(
     @NonNull ManagedObject object,
     /**
      * Provide a title for the target object, possibly abbreviated (according 
to supplied predicate)
-     * <p>
-     * One reason why the title might be abbreviated is if it is being 
evaluated in the context
+     *
+     * <p>One reason why the title might be abbreviated is if it is being 
evaluated in the context
      * of another object.
      * For example as a child object of a parented collection of some parent 
object.
      * In such a context, the title might be shortened so that it does not 
needlessly incorporate
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
index 3d569350f26..a6ebcf49ac5 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
@@ -44,13 +44,13 @@
 public class MetamodelInspectView extends MasterDetailTreeView<MMNode, 
MetamodelInspectView> {
 
     // -- FACTORY
-    
+
     public static MetamodelInspectView root(final ObjectSpecification spec) {
         return new MetamodelInspectView(new TypeNode(spec.logicalTypeName()), 
TreePath.root());
     }
 
     // -- CONSTRUCTION
-    
+
     private final Memento memento;
 
     public MetamodelInspectView(final String mementoString) {
@@ -66,7 +66,7 @@ public MetamodelInspectView(final String mementoString) {
         super(MMNode.class, rootNode, activeTreePath);
         this.memento = new Memento(rootNode.logicalName(), activeTreePath);
     }
-    
+
     // -- UI
 
     @ObjectSupport
@@ -75,13 +75,14 @@ public String title() {
     }
 
     @ObjectSupport
-    public String iconName() {
-        return activeNode().getClass().getSimpleName()
+    public ObjectSupport.IconResource icon(final ObjectSupport.IconWhere 
iconWhere) {
+        return new ObjectSupport.ClassPathIconResource(
+            activeNode().getClass().getSimpleName()
             + _Strings.nonEmpty(activeNode().iconName())
-                .map(suffix-> "-" + suffix)
-                .orElse("");
+            .map(suffix-> "-" + suffix)
+            .orElse(""));
     }
-    
+
     @Property(editingDisabledReason = "readonly by design")
     @PropertyLayout(labelPosition = LabelPosition.NONE, fieldSetId = "detail", 
sequence = "1")
     public Markup getDetails() {
@@ -89,7 +90,7 @@ public Markup getDetails() {
     }
 
     // -- IMPLEMENTATION DETAILS
-    
+
     @Override
     public String viewModelMemento() {
         return memento.stringify();
@@ -109,7 +110,7 @@ protected TreeAdapter<MMNode> treeAdapter() {
     }
 
     // -- HELPER
-    
+
     private record Memento (
         String logicalName,
         TreePath treePath) {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
index 1b23e8f2597..54f6c84d24a 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/ManagedObject.java
@@ -24,6 +24,7 @@
 import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.applib.services.repository.EntityState;
 import org.apache.causeway.commons.collections.Can;
@@ -32,7 +33,6 @@
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
-import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconService;
 import org.apache.causeway.core.metamodel.spec.HasObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter;
@@ -381,20 +381,13 @@ default EntityState getEntityState() {
 
     // -- SHORTCUT - ICON
 
-    /**
-     * Optionally returns the name-suffix (or embedded image data) of an icon 
to use for this object.
-     * @see ObjectIconService
-     */
-    default Optional<String> getIconName() {
-        return objSpec().getIconName(this);
-    }
-
     /**
      * Domain Objects may either have an icon corresponding to an icon 
resource,
      * or they use a font awesome icon.
+     * @param iconWhere
      */
-    default ObjectIcon getIcon() {
-        return objSpec().getIcon(this);
+    default ObjectIcon getIcon(IconWhere iconWhere) {
+        return getObjectIconService().getObjectIcon(this, iconWhere);
     }
 
     default Either<ManagedObject, ManagedObject> 
asEitherWithOrWithoutMemoizedBookmark() {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
index 9d8471f18d4..cf853b11228 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefault.java
@@ -19,12 +19,12 @@
 package org.apache.causeway.core.metamodel.services.title;
 
 import jakarta.annotation.Priority;
-import jakarta.inject.Inject;
 import jakarta.inject.Named;
 
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.services.title.TitleService;
 import org.apache.causeway.applib.services.wrapper.WrapperFactory;
@@ -34,8 +34,6 @@
 import org.apache.causeway.core.metamodel.object.MmEntityUtils;
 import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
 
-import lombok.RequiredArgsConstructor;
-
 /**
  * Default implementation of {@link TitleService}.
  *
@@ -45,17 +43,13 @@
 @Named(CausewayModuleCoreMetamodel.NAMESPACE + ".TitleServiceDefault")
 @Priority(PriorityPrecedence.MIDPOINT)
 @Qualifier("Default")
-@RequiredArgsConstructor(onConstructor_ = @Inject)
-public class TitleServiceDefault implements TitleService {
-
-    private final WrapperFactory wrapperFactory;
-    private final ObjectManager objectManager;
+public record TitleServiceDefault(
+    ObjectManager objectManager,
+    WrapperFactory wrapperFactory)
+implements TitleService {
 
     @Override
     public String titleOf(final Object domainObject) {
-
-        if(objectManager == null) return "" + domainObject; // simplified 
JUnit test support
-
         var pojo = unwrapped(domainObject);
         var objectAdapter = objectManager.adapt(pojo);
 
@@ -68,17 +62,14 @@ public String titleOf(final Object domainObject) {
     }
 
     @Override
-    public String iconNameOf(final Object domainObject) {
-
-        if(objectManager == null)  // simplified JUnit test support
-            return domainObject!=null ? 
domainObject.getClass().getSimpleName() : "null";
-
+    public ObjectSupport.IconResource iconOf(final Object domainObject, final 
ObjectSupport.IconWhere iconWhere) {
         var pojo = unwrapped(domainObject);
         var objectAdapter = objectManager.adapt(pojo);
 
         return ManagedObjects.isNullOrUnspecifiedOrEmpty(objectAdapter)
-            ? "unspecified"
-            : objectAdapter.objSpec().getIconName(objectAdapter).orElse(null);
+            ? null
+            : objectAdapter.objSpec().getIcon(objectAdapter, iconWhere)
+                .orElse(null);
     }
 
     //-- HELPER
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
index 90ba1bd210e..2e0f4b4e699 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
@@ -31,8 +31,8 @@
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.DomainService;
 import org.apache.causeway.applib.annotation.Introspection.IntrospectionPolicy;
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.applib.exceptions.UnrecoverableException;
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.applib.id.HasLogicalType;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.metamodel.BeanSort;
@@ -57,8 +57,6 @@
 import org.apache.causeway.core.metamodel.facets.collections.CollectionFacet;
 import 
org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
-import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
-import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconService;
 import 
org.apache.causeway.core.metamodel.facets.object.immutable.ImmutableFacet;
 import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet;
@@ -240,21 +238,19 @@ default Optional<MixedInAction> lookupMixedInAction(final 
ObjectSpecification mi
     /**
      * Returns the title to display of target adapter, rendered within the 
context
      * of some other adapter (if any).
-     * <p>
+     *
      * @see TitleFacet#title(TitleRenderRequest)
      */
     String getTitle(TitleRenderRequest titleRenderRequest);
 
     /**
-     * Optionally returns the name-suffix (or embedded image data) of an icon 
to use for the specified object.
-     * <p>
-     * Corresponds to the {@link IconFacet#iconName(ManagedObject) icon name}
-     * returned by the {@link IconFacet}; is not necessarily immutable.
+     * Optionally returns the resource definition of an icon to use for the 
specified object.
+     *
+     * <p>Corresponds to the icon(...) object support method.
      * @see ObjectIconService
+     * @since 4.0
      */
-    Optional<String> getIconName(ManagedObject object);
-
-    ObjectIcon getIcon(ManagedObject object);
+    Optional<ObjectSupport.IconResource> getIcon(ManagedObject object, 
ObjectSupport.IconWhere iconWhere);
 
     /**
      * Returns this object's navigable parent, if any.
@@ -273,8 +269,6 @@ default Optional<MixedInAction> lookupMixedInAction(final 
ObjectSpecification mi
      */
     String getCssClass(ManagedObject domainObject);
 
-    Optional<FontAwesomeLayers> getFaLayers(ManagedObject domainObject);
-
     /**
      * @return optionally the element type spec based on presence of the 
TypeOfFacet
      * @since 2.0
@@ -293,9 +287,7 @@ default Optional<MixedInAction> lookupMixedInAction(final 
ObjectSpecification mi
      */
     Optional<Contributing> contributing();
 
-    // //////////////////////////////////////////////////////////////
-    // TitleContext
-    // //////////////////////////////////////////////////////////////
+    // -- TITLE CONTEXT
 
     /**
      * Create an {@link InteractionContext} representing an attempt to read the
@@ -305,9 +297,7 @@ ObjectTitleContext createTitleInteractionContext(
             ManagedObject targetObjectAdapter,
             InteractionInitiatedBy invocationMethod);
 
-    // //////////////////////////////////////////////////////////////
-    // ValidityContext, Validity
-    // //////////////////////////////////////////////////////////////
+    // -- VALIDITY
 
     // internal API
     ObjectValidityContext createValidityInteractionContext(
@@ -330,9 +320,7 @@ InteractionResult isValidResult(
             final ManagedObject targetAdapter,
             final InteractionInitiatedBy interactionInitiatedBy);
 
-    // //////////////////////////////////////////////////////////////
-    // Facets
-    // //////////////////////////////////////////////////////////////
+    // -- FACETS
 
     /**
      * Determines if the object represents an value or object.
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
index 442df8b7344..e1718fa11c1 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java
@@ -39,7 +39,7 @@
 import org.apache.causeway.applib.annotation.Domain;
 import org.apache.causeway.applib.annotation.DomainService;
 import org.apache.causeway.applib.annotation.Introspection.IntrospectionPolicy;
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.metamodel.BeanSort;
 import org.apache.causeway.commons.collections.Can;
@@ -79,11 +79,8 @@
 import 
org.apache.causeway.core.metamodel.facets.all.named.MemberNamedFacetForStaticMemberName;
 import org.apache.causeway.core.metamodel.facets.all.named.ObjectNamedFacet;
 import 
org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet;
-import org.apache.causeway.core.metamodel.facets.members.iconfa.FaFacet;
-import 
org.apache.causeway.core.metamodel.facets.members.iconfa.FaLayersProvider;
 import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
-import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import 
org.apache.causeway.core.metamodel.facets.object.immutable.ImmutableFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.introspection.IntrospectionPolicyFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.logicaltype.AliasedFacet;
@@ -771,30 +768,14 @@ public String getTitle(final TitleRenderRequest 
titleRenderRequest) {
     private void notifySubscribersIfEntity(
             final TitleRenderRequest titleRenderRequest,
             final String titleString) {
-        if (!isEntity()) {
-            return;
-        }
+        if (!isEntity()) return;
+
         var managedObject = titleRenderRequest.object();
         managedObject.getBookmark().ifPresent(bookmark -> {
             getTitleSubscribers().stream().forEach(x -> 
x.entityTitleIs(bookmark, titleString));
         });
     }
 
-    @Override
-    public Optional<String> getIconName(final ManagedObject domainObject) {
-        if(ManagedObjects.isSpecified(domainObject)) {
-            _Assert.assertEquals(domainObject.objSpec(), this);
-        }
-        return Optional.ofNullable(iconFacet)
-                .flatMap(facet->facet.iconName(domainObject));
-    }
-
-    @Override
-    public ObjectIcon getIcon(final ManagedObject domainObject) {
-        return getObjectIconService()
-            .getObjectIcon(this, getIconName(domainObject), 
getFaLayers(domainObject));
-    }
-
     @Override
     public Object getNavigableParent(final Object object) {
         return navigableParentFacet != null
@@ -809,16 +790,6 @@ public String getCssClass(final ManagedObject reference) {
                 : null;
     }
 
-    @Override
-    public Optional<FontAwesomeLayers> getFaLayers(final ManagedObject 
reference){
-        return lookupFacet(FaFacet.class)
-                .map(FaFacet::getSpecialization)
-                .map(either->either.fold(
-                        faStaticFacet->(FaLayersProvider)faStaticFacet,
-                        
faImperativeFacet->faImperativeFacet.getFaLayersProvider(reference)))
-                .map(FaLayersProvider::getLayers);
-    }
-
     @Override
     public Can<LogicalType> getAliases() {
         return aliasedFacet != null
@@ -826,6 +797,17 @@ public Can<LogicalType> getAliases() {
                 : Can.empty();
     }
 
+    // -- ICON
+
+    @Override
+    public Optional<ObjectSupport.IconResource> getIcon(final ManagedObject 
domainObject, ObjectSupport.IconWhere iconWhere) {
+        if(ManagedObjects.isSpecified(domainObject)) {
+            _Assert.assertEquals(domainObject.objSpec(), this);
+        }
+        return Optional.ofNullable(iconFacet)
+            .flatMap(facet->facet.icon(domainObject, iconWhere));
+    }
+
     // -- HIERARCHICAL
 
     @Override
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
index 34696de3e6b..6e284489ccb 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
@@ -433,8 +433,8 @@ protected void objectScenario(final Class<?> 
declaringClass, final BiConsumer<Pr
 
     // -- UTILITY
 
-    protected static ResolvedMethod findMethodExactOrFail(final Class<?> type, 
final String methodName, final Class<?>[] methodTypes) {
-        return _Utils.findMethodExactOrFail(type, methodName, methodTypes);
+    protected static ResolvedMethod findMethodExactOrFail(final Class<?> type, 
final String methodName, final Class<?>[] paramTypes) {
+        return _Utils.findMethodExactOrFail(type, methodName, paramTypes);
     }
 
     protected static ResolvedMethod findMethodExactOrFail(final Class<?> type, 
final String methodName) {
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFaTest.java
similarity index 66%
rename from 
core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodTest.java
rename to 
core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFaTest.java
index 20349d92711..cf730cde426 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFaTest.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.causeway.core.metamodel.facets.object.ident.iconfa;
+package org.apache.causeway.core.metamodel.facets.object.ident.icon;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -29,21 +29,22 @@
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.Introspection;
 import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.core.metamodel.facets.FacetFactoryTestAbstract;
-import org.apache.causeway.core.metamodel.facets.members.iconfa.FaFacet;
-import 
org.apache.causeway.core.metamodel.facets.object.iconfa.method.FaFacetViaIconFaLayersMethod;
+import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
+import 
org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod;
 import 
org.apache.causeway.core.metamodel.facets.object.support.ObjectSupportFacetFactory;
 
-class FontAwesomeLayersFacetMethodTest
+class IconFacetMethodFaTest
 extends FacetFactoryTestAbstract {
 
     static final FontAwesomeLayers FONTAWESOME_LAYERS_SAMPLE = 
FontAwesomeLayers.singleIcon("fa-solid fa-bookmark");
 
     @DomainObject(introspection = Introspection.ENCAPSULATION_ENABLED)
-    static class DomainObjectWithFontAwesomeLayersMethod {
-        @ObjectSupport public FontAwesomeLayers iconFaLayers() {
-            return FONTAWESOME_LAYERS_SAMPLE;
+    static class DomainObjectWithFontAwesomeIconViaMethod {
+        @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+            return new 
ObjectSupport.FontAwesomeIconResource(FONTAWESOME_LAYERS_SAMPLE);
         }
     }
 
@@ -61,19 +62,23 @@ protected void tearDown() throws Exception {
     }
 
     @Test
-    void fontAwesomeLayersFacetViaIconFaLayersMethod() {
-        var domainObject = getObjectManager().adapt(new 
DomainObjectWithFontAwesomeLayersMethod());
+    void fontAwesomeLayersViaIconMethod() {
+        var domainObject = getObjectManager().adapt(new 
DomainObjectWithFontAwesomeIconViaMethod());
 
-        objectScenario(DomainObjectWithFontAwesomeLayersMethod.class, 
(processClassContext, facetHolder) -> {
+        objectScenario(DomainObjectWithFontAwesomeIconViaMethod.class, 
(processClassContext, facetHolder) -> {
             //when
             facetFactory.process(processClassContext);
             //then
-            var fontAwesomeLayersFacet = facetHolder.getFacet(FaFacet.class);
-            assertNotNull(fontAwesomeLayersFacet, ()->"FaFacet required");
-            assertTrue(fontAwesomeLayersFacet instanceof 
FaFacetViaIconFaLayersMethod);
-            var imperativeCssClassFacet = 
(FaFacetViaIconFaLayersMethod)fontAwesomeLayersFacet;
+            var iconFacet = facetHolder.getFacet(IconFacet.class);
+            assertNotNull(iconFacet, ()->"IconFacet required");
+            assertTrue(iconFacet instanceof IconFacetViaIconMethod);
+            var imperativeFacet = (IconFacetViaIconMethod)iconFacet;
 
-            var actual = 
imperativeCssClassFacet.getFaLayersProvider(domainObject).getLayers();
+            var actual = imperativeFacet.icon(domainObject, 
IconWhere.OBJECT_HEADER)
+                
.filter(ObjectSupport.FontAwesomeIconResource.class::isInstance)
+                .map(ObjectSupport.FontAwesomeIconResource.class::cast)
+                .map(ObjectSupport.FontAwesomeIconResource::faLayers)
+                .orElse(null);
             assertEquals(FONTAWESOME_LAYERS_SAMPLE, actual);
             assertEquals(
                     FontAwesomeLayers.normalizeCssClasses("fa-solid 
fa-bookmark", "fa"),
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
index d56ae9f9f35..b80dc103263 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
@@ -20,6 +20,7 @@
 
 import org.junit.jupiter.api.Test;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
 import 
org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.ObjectSupportMethod;
 import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.support.ObjectSupportFacetFactoryTestAbstract;
@@ -29,12 +30,12 @@ class IconFacetMethodFactoryTest
 
     @Test
     void iconNameMethodPickedUpOnClassAndMethodRemoved() {
-        @SuppressWarnings("unused")
         class Customer {
-            public String iconName() { return null; }
-
+            @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+                return null;
+            }
         }
-        assertPicksUp(1, facetFactory, Customer.class, 
ObjectSupportMethod.ICON_NAME, IconFacet.class);
+        assertPicksUp(1, facetFactory, Customer.class, 
ObjectSupportMethod.ICON, IconFacet.class, ObjectSupport.IconWhere.class);
     }
 
 }
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
index 2a797165dfd..f4b7124f21c 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
@@ -27,22 +27,24 @@
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.commons.internal.reflection._GenericResolver;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.Mocking;
-import 
org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod;
+import 
org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 
 class IconFacetMethodTest {
 
     private Mocking mocking = new Mocking();
-    private IconFacetViaIconNameMethod facet;
+    private IconFacetViaIconMethod facet;
     private ManagedObject mockOwningAdapter;
 
     private DomainObjectWithProblemInIconNameMethod pojo;
 
     public static class DomainObjectWithProblemInIconNameMethod {
-        public String iconName() {
+        @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
             throw new NullPointerException("for testing purposes");
         }
     }
@@ -51,11 +53,11 @@ public String iconName() {
     public void setUp() throws Exception {
 
         pojo = new DomainObjectWithProblemInIconNameMethod();
-        var iconNameMethod = _GenericResolver.testing
-                .resolveMethod(DomainObjectWithProblemInIconNameMethod.class, 
"iconName");
+        var iconMethod = _GenericResolver.testing
+                .resolveMethod(DomainObjectWithProblemInIconNameMethod.class, 
"icon", ObjectSupport.IconWhere.class);
 
-        facet = (IconFacetViaIconNameMethod) IconFacetViaIconNameMethod
-                .create(iconNameMethod, Mockito.mock(FacetHolder.class))
+        facet = (IconFacetViaIconMethod) IconFacetViaIconMethod
+                .create(iconMethod, Mockito.mock(FacetHolder.class))
                 .orElse(null);
 
         mockOwningAdapter = mocking.asViewmodel(pojo);
@@ -67,10 +69,9 @@ public void tearDown() throws Exception {
     }
 
     @Test
-    void iconNameThrowsException() {
-        //assertThrows(Exception.class, ()->facet.iconName(mockOwningAdapter));
-        final String iconName = facet.iconName(mockOwningAdapter).orElse(null);
-        assertThat(iconName, is(nullValue()));
+    void iconIsNull() {
+        var icon = facet.icon(mockOwningAdapter, 
IconWhere.OBJECT_HEADER).orElse(null);
+        assertThat(icon, is(nullValue()));
     }
 
 }
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodFactoryTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodFactoryTest.java
deleted file mode 100644
index 69a4dbf67ba..00000000000
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/ident/iconfa/FontAwesomeLayersFacetMethodFactoryTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.core.metamodel.facets.object.ident.iconfa;
-
-import org.junit.jupiter.api.Test;
-
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
-import 
org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.ObjectSupportMethod;
-import org.apache.causeway.core.metamodel.facets.members.iconfa.FaFacet;
-import 
org.apache.causeway.core.metamodel.facets.object.support.ObjectSupportFacetFactoryTestAbstract;
-
-class FontAwesomeLayersFacetMethodFactoryTest
-extends ObjectSupportFacetFactoryTestAbstract {
-
-    @Test
-    void iconNameMethodPickedUpOnClassAndMethodRemoved() {
-        @SuppressWarnings("unused")
-        class Customer {
-            public FontAwesomeLayers iconFaLayers() { return null; }
-        }
-        assertPicksUp(1, facetFactory, Customer.class, 
ObjectSupportMethod.ICON_FA_LAYERS, FaFacet.class);
-    }
-
-}
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactoryTestAbstract.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactoryTestAbstract.java
index 7a461e5e763..086a9e17bf6 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactoryTestAbstract.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/support/ObjectSupportFacetFactoryTestAbstract.java
@@ -56,14 +56,15 @@ protected void assertPicksUp(
             final FacetFactory facetFactory,
             final Class<?> type,
             final ProgrammingModelConstants.ObjectSupportMethod 
supportMethodEnum,
-            final Class<? extends Facet> facetType) {
+            final Class<? extends Facet> facetType,
+            final Class<?> ...paramTypes) {
 
         objectScenario(type, (processClassContext, facetHolder) -> {
             //when
             facetFactory.process(processClassContext);
             //then
             var supportMethods = supportMethodEnum.getMethodNames()
-                    .map(methodName->findMethodExactOrFail(type, methodName))
+                    .map(methodName->findMethodExactOrFail(type, methodName, 
paramTypes))
                     .map(_MethodFacades::regular)
                     .map(MethodFacade::asMethodElseFail);
 
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
index 2f94fa99d8c..acf798ac3c3 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/title/TitleServiceDefaultTest.java
@@ -34,7 +34,7 @@ void setUp() throws Exception {
 
         var mmc = MetaModelContext_forTesting.buildDefault();
 
-        titleService = new TitleServiceDefault(null, mmc.getObjectManager());
+        titleService = new TitleServiceDefault(mmc.getObjectManager(), null);
     }
 
     // -- FEATURED
@@ -60,7 +60,6 @@ void enum_shouldHonorTitleByMethod() {
 
         var title = titleService.titleOf(domainObject);
         assertEquals("first", title);
-
     }
 
     // -- PLAIN
@@ -77,7 +76,6 @@ void enum_shouldFallbackTitleToEnumName() {
 
         var title = titleService.titleOf(domainObject);
         assertEquals("First", title);
-
     }
 
 }
diff --git 
a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
index caa24d65fbf..b170e7e1d3f 100644
--- 
a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
+++ 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
@@ -96,7 +96,6 @@
 import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault;
 import org.apache.causeway.core.metamodel.services.layout.LayoutServiceDefault;
 import org.apache.causeway.core.metamodel.services.message.MessageServiceNoop;
-import org.apache.causeway.core.metamodel.services.title.TitleServiceDefault;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import 
org.apache.causeway.core.metamodel.spec.impl.CausewayModuleCoreMetamodelConfigurationDefault;
 import org.apache.causeway.core.metamodel.spec.impl._JUnitSupport;
@@ -203,7 +202,7 @@ public CausewayConfiguration getConfiguration() {
     private AuthenticationManager authenticationManager;
 
     @Builder.Default @Getter
-    private TitleService titleService = new TitleServiceDefault(null, null);
+    private TitleService titleService = new TitleServiceForTesting();
 
     @Getter
     private ObjectIconService objectIconService;
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacetAbstract.java
 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/TitleServiceForTesting.java
similarity index 56%
rename from 
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacetAbstract.java
rename to 
core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/TitleServiceForTesting.java
index 3931dabe151..528e8b1a29c 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/IconFacetAbstract.java
+++ 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/TitleServiceForTesting.java
@@ -16,26 +16,23 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.causeway.core.metamodel.facets.object.icon;
+package org.apache.causeway.core.mmtestsupport;
 
-import org.apache.causeway.core.metamodel.facetapi.Facet;
-import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
-import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import 
org.apache.causeway.applib.annotation.ObjectSupport.ClassPathIconResource;
+import org.apache.causeway.applib.services.title.TitleService;
 
-public abstract class IconFacetAbstract
-extends FacetAbstract
-implements IconFacet {
+public record TitleServiceForTesting()
+implements TitleService {
 
-    private static final Class<? extends Facet> type() {
-        return IconFacet.class;
+    @Override
+    public String titleOf(final Object domainObject) {
+        return "" + domainObject; // simplified JUnit test support
     }
 
-    public IconFacetAbstract(final FacetHolder holder) {
-        super(type(), holder);
-    }
-
-    public IconFacetAbstract(final FacetHolder holder, final Facet.Precedence 
precedence) {
-        super(type(), holder, precedence);
+    @Override
+    public ObjectSupport.IconResource iconOf(final Object domainObject, final 
ObjectSupport.IconWhere iconWhere) {
+        return domainObject!=null ? new ClassPathIconResource("") : null;
     }
 
 }
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
index 0bd5bd6c1a3..e4e91c5f1c9 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
@@ -28,25 +28,31 @@
 import jakarta.inject.Named;
 
 import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.core.io.ResourceLoader;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import 
org.apache.causeway.applib.annotation.ObjectSupport.EmbeddedIconResource;
+import 
org.apache.causeway.applib.annotation.ObjectSupport.FontAwesomeIconResource;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
+import 
org.apache.causeway.applib.annotation.ObjectSupport.ClassPathIconResource;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._StableValue;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.commons.internal.resources._Resources;
-import org.apache.causeway.commons.net.DataUri;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import 
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconEmbedded;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconFa;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconService;
 import 
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconUrlBased;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import 
org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices;
 
@@ -63,7 +69,7 @@
 record ObjectIconServiceDefault(
         ResourceLoader resourceLoader,
         Map<String, ObjectIcon> iconByKey,
-        _StableValue<ObjectIcon> fallbackIcon)
+        _StableValue<ObjectIcon> fallbackIconRef)
 implements ObjectIconService {
 
     private static final String DEFAULT_IMAGE_RESOURCE_PATH = 
"classpath:images";
@@ -81,53 +87,58 @@ public ObjectIconServiceDefault(ResourceLoader 
resourceLoader) {
     }
 
     @Override
-    public ObjectIcon getObjectIcon(
-            final ObjectSpecification spec,
-            final Optional<String> iconName,
-            final Optional<FontAwesomeLayers> faLayers) {
+    public ObjectIcon getObjectIcon(ManagedObject managedObject, IconWhere 
iconWhere) {
+
+        var spec = managedObject.objSpec();
+
+        return spec.getIcon(managedObject, iconWhere)
+            .map(iconResource->{
+                if(iconResource instanceof ObjectSupport.EmbeddedIconResource 
embedded)
+                    return embedded(spec, embedded);
+                if(iconResource instanceof 
ObjectSupport.FontAwesomeIconResource fa)
+                    return fa(spec, fa);
+                if(iconResource instanceof ObjectSupport.ClassPathIconResource 
suffixed)
+                    return suffixed(spec, suffixed);
+                throw _Exceptions.unmatchedCase(iconResource);
+            })
+            .orElseGet(this::fallbackIcon);
+    }
 
-        var domainClass = spec.getCorrespondingClass();
-        var iconNameSuffixIfAny = iconName.orElse(null);
+    // -- HELPER
 
-        var suffix = "";
-        if(StringUtils.hasLength(iconNameSuffixIfAny)) {
-            if(iconNameSuffixIfAny.startsWith("data:")) {
-                return new ObjectIconEmbedded(domainClass.getSimpleName(), 
DataUri.parse(iconNameSuffixIfAny));
-            }
-            suffix = "-" + iconNameSuffixIfAny;
-        } else if(faLayers.isPresent()) {
-            return new ObjectIconFa(domainClass.getSimpleName(), 
faLayers.get());
-        }
+    private ObjectIcon embedded(ObjectSpecification objSpec, 
EmbeddedIconResource embeddedIconResource) {
+        return new 
ObjectIconEmbedded(objSpec.getCorrespondingClass().getSimpleName(), 
embeddedIconResource.dataUri());
+    }
 
-        var iconResourceKey = domainClass.getName() + suffix;
+    private ObjectIcon fa(ObjectSpecification objSpec, FontAwesomeIconResource 
faIconResource) {
+        return new 
ObjectIconFa(objSpec.getCorrespondingClass().getSimpleName(), 
faIconResource.faLayers());
+    }
 
+    private ObjectIcon suffixed(ObjectSpecification objSpec, 
ClassPathIconResource cpIconResource) {
+        var domainClass = objSpec.getCorrespondingClass();
+        var iconResourceKey = StringUtils.hasLength(cpIconResource.suffix())
+            ? domainClass.getName() + "-" + cpIconResource.suffix()
+            : domainClass.getName();
         // also memoize unsuccessful icon lookups (as fallback), so we don't 
search repeatedly
-
         var cachedIcon = iconByKey.get(iconResourceKey);
         if(cachedIcon!=null) return cachedIcon;
 
-        var icon = findIcon(spec, iconName);
-
-        //NOTE: cannot use computeIfAbsent, as it does not support recursive 
update
-        // return iconByKey.computeIfAbsent(iconResourceKey, key->
-        //     findIcon(spec, iconNameModifier));
+        var icon = findIcon(objSpec, 
_Strings.nonEmpty(cpIconResource.suffix()));
         iconByKey.put(iconResourceKey, icon);
-
         return icon;
     }
 
-    // -- HELPER
-
-    private ObjectIcon getObjectFallbackIcon() {
-        return fallbackIcon.orElseSet(()->ObjectIconUrlBased.eager(
+    private ObjectIcon fallbackIcon() {
+        return fallbackIconRef.orElseSet(()->ObjectIconUrlBased.eager(
                 "ObjectIconFallback",
                 _Resources.lookupResourceUrl(
                         ObjectIconServiceDefault.class,
                         "ObjectIconFallback.png")
-                .orElse(null),
+                    .orElse(null),
                 CommonMimeType.PNG));
     }
 
+    @Nullable
     private ObjectIcon findIcon(
             final ObjectSpecification spec,
             final Optional<String> iconName) {
@@ -145,7 +156,7 @@ private ObjectIcon findIcon(
             var objectIcon = imageType
                 .proposedFileExtensions()
                 .stream()
-                .map(suffix->iconResourceNameNoExt + "." + suffix)
+                .map(ext->iconResourceNameNoExt + "." + ext)
                 .map(iconResourceName->
                         classPathResource(domainClass, iconResourceName)
                         .map(url->ObjectIconUrlBased.lazy(
@@ -182,14 +193,13 @@ private ObjectIcon findIcon(
 
         return spec.superclass()!=null
             // continue search in super spec
-            ? getObjectIcon(spec.superclass(), iconName, Optional.empty()) // 
memoizes as a side-effect
+            ? findIcon(spec.superclass(), iconName) // memoizes as a 
side-effect
             : _Strings.isNotEmpty(iconNameSuffixIfAny)
                 // also do a more generic search, skipping the modifier
-                ? getObjectIcon(spec, Optional.empty(), Optional.empty()) // 
memoizes as a side-effect
-                : getObjectFallbackIcon();
+                ? findIcon(spec, Optional.empty()) // memoizes as a side-effect
+                : null;
     }
 
-    // -- HELPER
 
     @SneakyThrows
     private Optional<URL> classPathResource(
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyAbstract.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyAbstract.java
index 8cbce54516d..1a40784047f 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyAbstract.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyAbstract.java
@@ -31,7 +31,7 @@
 abstract class ProperFullyAbstract {
 
     @ObjectSupport public abstract String title();
-    @ObjectSupport public abstract String iconName();
+    @ObjectSupport public abstract ObjectSupport.IconResource 
icon(ObjectSupport.IconWhere iconWhere);
     @ObjectSupport public abstract String cssClass();
     @ObjectSupport public abstract String layout();
 
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyImpl.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyImpl.java
index 39b588bc53b..d5de33dce25 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyImpl.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperFullyImpl.java
@@ -22,6 +22,7 @@
 
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.Nature;
+import org.apache.causeway.applib.annotation.ObjectSupport;
 
 @DomainObject(nature = Nature.VIEW_MODEL)
 public class ProperFullyImpl
@@ -33,8 +34,8 @@ public String title() {
     }
 
     @Override
-    public String iconName() {
-        return "icon";
+    public ObjectSupport.IconResource icon(final ObjectSupport.IconWhere 
iconWhere) {
+        return new ObjectSupport.ClassPathIconResource("icon");
     }
 
     @Override
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceAbstract.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceAbstract.java
index 1b9047c17da..f7fbb3d86dd 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceAbstract.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceAbstract.java
@@ -37,8 +37,8 @@ abstract class ProperMemberInheritanceAbstract {
         return "inherited title";
     }
 
-    @ObjectSupport public String iconName() {
-        return "inherited icon";
+    @ObjectSupport public ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+        return new ObjectSupport.ClassPathIconResource("inherited icon");
     }
 
     @ObjectSupport public String cssClass(){
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceInterface.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceInterface.java
index c02d0c1ec0a..d70b7aca363 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceInterface.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/good/ProperMemberInheritanceInterface.java
@@ -35,9 +35,8 @@ default String title() {
         return "inherited title";
     }
 
-    @ObjectSupport
-    default String iconName() {
-        return "inherited icon";
+    @ObjectSupport default ObjectSupport.IconResource icon(final 
ObjectSupport.IconWhere iconWhere) {
+        return new ObjectSupport.ClassPathIconResource("inherited icon");
     }
 
     @Action
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
index ae05a7f37d0..d9e947164aa 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/util/interaction/DomainObjectTesterFactory.java
@@ -40,6 +40,8 @@
 import org.springframework.stereotype.Service;
 
 import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.annotation.Where;
 import 
org.apache.causeway.applib.exceptions.unrecoverable.DomainModelException;
 import org.apache.causeway.applib.id.LogicalType;
@@ -225,11 +227,12 @@ public void assertTitle(final @Nullable String 
expectedResult) {
         }
 
         public void assertIcon(final @Nullable String expectedResult) {
-            assertEquals(expectedResult,
-                    
super.objectSpecification.getTitleService().iconNameOf(vm.getPojo()));
-            assertEquals(expectedResult,
+            var expectedIcon = new 
ObjectSupport.ClassPathIconResource(expectedResult);
+            assertEquals(expectedIcon,
+                    
super.objectSpecification.getTitleService().iconOf(vm.getPojo(), 
IconWhere.OBJECT_HEADER));
+            assertEquals(expectedIcon,
                     super.objectSpecification.lookupFacet(IconFacet.class)
-                    .flatMap(iconFacet->iconFacet.iconName(vm))
+                    .flatMap(iconFacet->iconFacet.icon(vm, 
IconWhere.OBJECT_HEADER))
                     .orElse(null));
         }
 
diff --git 
a/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index d4ed1c8f53a..2e1d5d05516 100644
--- 
a/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -44,6 +44,8 @@
 
 import org.apache.causeway.applib.annotation.Introspection.EncapsulationPolicy;
 import 
org.apache.causeway.applib.annotation.Introspection.MemberAnnotationPolicy;
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.jaxb.JaxbService;
 import org.apache.causeway.applib.services.metamodel.BeanSort;
@@ -292,11 +294,15 @@ void titleAndIconName_shouldBeInheritable(final Class<?> 
type) throws Exception
         if(!spec.isAbstract()) {
             var instance = type.getDeclaredConstructor().newInstance();
             assertEquals("inherited title", titleService.titleOf(instance));
-            assertEquals("inherited icon", titleService.iconNameOf(instance));
+            assertEquals(
+                new ObjectSupport.ClassPathIconResource("inherited icon"),
+                titleService.iconOf(instance, IconWhere.OBJECT_HEADER));
 
             var domainObject = ManagedObject.adaptSingular(spec, instance);
             assertEquals("inherited title", domainObject.getTitle());
-            assertEquals("inherited icon", 
iconFacet.iconName(domainObject).orElse(null));
+            assertEquals(
+                new ObjectSupport.ClassPathIconResource("inherited icon"),
+                iconFacet.icon(domainObject, 
IconWhere.OBJECT_HEADER).orElse(null));
         }
     }
 
diff --git 
a/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
index b6005b58e71..7f4840dc116 100644
--- 
a/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
+++ 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/MetaModelRegressionTest.verify.approved.xml
@@ -9229,10 +9229,10 @@
                 <mml:attr name="interactionAdvisors" value="HidingOrShowing"/>
                 <mml:attr name="precedence" value="HIGH"/>
             </mml:facet>
-            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod">
-                <mml:attr name="facet" value="IconFacetViaIconNameMethod"/>
-                <mml:attr name="intent.iconName" value="UI_HINT"/>
-                <mml:attr name="methods" value="public java.lang.String 
org.apache.causeway.testdomain.model.good.ProperFullyImpl.iconName()"/>
+            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod">
+                <mml:attr name="facet" value="IconFacetViaIconMethod"/>
+                <mml:attr name="intent.icon" value="UI_HINT"/>
+                <mml:attr name="methods" value="public 
org.apache.causeway.applib.annotation.ObjectSupport$IconResource 
org.apache.causeway.testdomain.model.good.ProperFullyImpl.icon(org.apache.causeway.applib.annotation.ObjectSupport$IconWhere)"/>
                 <mml:attr name="precedence" value="DEFAULT"/>
             </mml:facet>
             <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacetViaMethod">
@@ -13567,10 +13567,10 @@
                 <mml:attr name="interactionAdvisors" value="HidingOrShowing"/>
                 <mml:attr name="precedence" value="HIGH"/>
             </mml:facet>
-            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod">
-                <mml:attr name="facet" value="IconFacetViaIconNameMethod"/>
-                <mml:attr name="intent.iconName" value="UI_HINT"/>
-                <mml:attr name="methods" value="public java.lang.String 
org.apache.causeway.testdomain.model.good.ProperMemberInheritanceAbstract.iconName()"/>
+            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod">
+                <mml:attr name="facet" value="IconFacetViaIconMethod"/>
+                <mml:attr name="intent.icon" value="UI_HINT"/>
+                <mml:attr name="methods" value="public 
org.apache.causeway.applib.annotation.ObjectSupport$IconResource 
org.apache.causeway.testdomain.model.good.ProperMemberInheritanceAbstract.icon(org.apache.causeway.applib.annotation.ObjectSupport$IconWhere)"/>
                 <mml:attr name="precedence" value="DEFAULT"/>
             </mml:facet>
             <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacetViaMethod">
@@ -14915,10 +14915,10 @@
                 <mml:attr name="interactionAdvisors" value="HidingOrShowing"/>
                 <mml:attr name="precedence" value="HIGH"/>
             </mml:facet>
-            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod">
-                <mml:attr name="facet" value="IconFacetViaIconNameMethod"/>
-                <mml:attr name="intent.iconName" value="UI_HINT"/>
-                <mml:attr name="methods" value="public default 
java.lang.String 
org.apache.causeway.testdomain.model.good.ProperMemberInheritanceInterface.iconName()"/>
+            <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.icon.IconFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.icon.method.IconFacetViaIconMethod">
+                <mml:attr name="facet" value="IconFacetViaIconMethod"/>
+                <mml:attr name="intent.icon" value="UI_HINT"/>
+                <mml:attr name="methods" value="public default 
org.apache.causeway.applib.annotation.ObjectSupport$IconResource 
org.apache.causeway.testdomain.model.good.ProperMemberInheritanceInterface.icon(org.apache.causeway.applib.annotation.ObjectSupport$IconWhere)"/>
                 <mml:attr name="precedence" value="DEFAULT"/>
             </mml:facet>
             <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.objectvalidprops.ObjectValidPropertiesFacet"
 
fqcn="org.apache.causeway.core.metamodel.facets.object.objectvalidprops.impl.ObjectValidPropertiesFacetImpl">
diff --git 
a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/mixin/HasIcon.java
 
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/mixin/HasIcon.java
index e02226fd958..2b19cc8f6df 100644
--- 
a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/mixin/HasIcon.java
+++ 
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/mixin/HasIcon.java
@@ -21,6 +21,7 @@
 import java.util.Objects;
 import java.util.function.Consumer;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import 
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconEmbedded;
@@ -30,14 +31,15 @@
 @FunctionalInterface
 public interface HasIcon {
 
-    ObjectIcon getIcon();
+    ObjectIcon getIcon(IconWhere iconWhere);
 
     default void visitIconVariant(
+        IconWhere iconWhere,
         Consumer<ObjectIconUrlBased> a,
         Consumer<ObjectIconEmbedded> b,
         Consumer<ObjectIconFa> c) {
 
-        var objectIcon = Objects.requireNonNull(getIcon());
+        var objectIcon = Objects.requireNonNull(getIcon(iconWhere));
         if(objectIcon instanceof ObjectIconUrlBased urlBased){
             a.accept(urlBased);
         } else if(objectIcon instanceof ObjectIconEmbedded embedded){
diff --git 
a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
 
b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
index bf371186770..e10fff67c05 100644
--- 
a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
+++ 
b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
@@ -36,6 +36,7 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.applib.services.bookmark.BookmarkService;
@@ -174,7 +175,7 @@ public ResponseEntity<byte[]> icon(
         }
 
         return lookup(logicalTypeName, id)
-                .map(ManagedObject::getIcon)
+                .map(mo->mo.getIcon(IconWhere.OBJECT_HEADER))
                 .filter(Objects::nonNull)
                 .map(objectIcon -> {
                     var bytes = objectIcon.iconData();
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
index 615c30f048f..7312f45031f 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
@@ -27,6 +27,7 @@
 import org.springframework.web.bind.annotation.RestController;
 
 import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.layout.component.ActionLayoutData;
 import org.apache.causeway.applib.layout.component.CollectionLayoutData;
 import org.apache.causeway.applib.layout.component.DomainObjectLayoutData;
@@ -218,7 +219,7 @@ public ResponseEntity<Object> image(
 
         var objectAdapter = getObjectAdapterElseThrowNotFound(domainType, 
instanceId,
                 roEx->_EndpointLogging.error(log, "GET 
/objects/{}/{}/object-icon", domainType, instanceId, roEx));
-        var objectIcon = objectAdapter.getIcon();
+        var objectIcon = objectAdapter.getIcon(IconWhere.OBJECT_HEADER);
 
         return _EndpointLogging.response(log, "GET 
/objects/{}/{}/object-icon", domainType, instanceId,
             responseFactory.ok(
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
index 52fb4a6696f..4d287f581b3 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
@@ -28,6 +28,7 @@
 import org.apache.wicket.request.resource.ResourceReference;
 import org.jspecify.annotations.NonNull;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.commons.internal.base._Casts;
@@ -75,6 +76,7 @@ private BookmarkTreeNode(
 
         _Casts.castTo(UiObjectWkt.class, bookmarkableModel)
         .ifPresent(x->x.visitIconVariantOrElse(
+                IconWhere.TABLE_ROW,
                 rref->{this.iconResourceReference = rref;},
                 embedded->{this.embedded = embedded;},
                 faLayers->{this.faLayers = faLayers;},
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkableModel.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkableModel.java
index f4bdcd261a4..63a08db93a6 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkableModel.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkableModel.java
@@ -24,6 +24,7 @@
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
 import org.apache.causeway.applib.annotation.BookmarkPolicy;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.causeway.viewer.commons.model.mixin.HasIcon;
@@ -65,7 +66,7 @@ default Stream<Bookmark> streamPropertyBookmarks() {
      * I believe actions only support the former. Hence the asymmetry here.
      */
     @Override
-    default ObjectIcon getIcon() {
+    default ObjectIcon getIcon(IconWhere iconWhere) {
         return null; // overwritten for domain objects
     }
 
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
index 30b7fd68235..ae48661a268 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
@@ -31,6 +31,7 @@
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import 
org.apache.causeway.applib.exceptions.unrecoverable.ObjectNotFoundException;
 import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
@@ -184,16 +185,18 @@ public String getTitle() {
     }
 
     @Override
-    public ObjectIcon getIcon() {
-        return getManagedObject().getIcon();
+    public ObjectIcon getIcon(IconWhere iconWhere) {
+        return getManagedObject().getIcon(iconWhere);
     }
 
     public void visitIconVariantOrElse(
+            IconWhere iconWhere,
             Consumer<ResourceReference> a,
             Consumer<ObjectIconEmbedded> b,
             Consumer<FontAwesomeLayers> c,
             Runnable onNoMatch) {
         visitIconVariant(
+            iconWhere,
             urlBased->{
                 var rref = 
imageResourceCache().resourceReferenceForObjectIcon(urlBased);
                 if(rref!=null) {
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
index 691e20d72e6..f1f181c6ac3 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
@@ -98,11 +98,10 @@ private void buildGui() {
         final String actionIdToUse;
         final String actionIdToHide;
         if(domainObject != null) {
-            final WebMarkupContainer entityHeaderPanel = new 
WebMarkupContainer(ID_OBJECT_HEADER_PANEL);
+            var entityHeaderPanel = new 
WebMarkupContainer(ID_OBJECT_HEADER_PANEL);
             div.add(entityHeaderPanel);
-            final ComponentFactory componentFactory =
-                    
getComponentFactoryRegistry().findComponentFactory(UiComponentType.OBJECT_ICON_TITLE_AND_COPYLINK,
 getModel());
-            final Component component = 
componentFactory.createComponent(getModel());
+            final Component component = getComponentFactoryRegistry()
+                
.createComponent(UiComponentType.OBJECT_ICON_TITLE_AND_COPYLINK, getModel());
             entityHeaderPanel.addOrReplace(component);
 
             actionOwner = entityHeaderPanel;
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
index 80fc51d0860..0fd4cd24784 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
@@ -24,6 +24,7 @@
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.markup.html.link.AbstractLink;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.applib.layout.component.CssClassFaPosition;
 import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.base._Strings;
@@ -61,10 +62,14 @@ class ObjectIconAndTitlePanel
     private static final String ID_OBJECT_TITLE = "objectTitle";
     private static final String ID_OBJECT_ICON = "objectImage";
 
+    private IconWhere iconWhere;
+
     public ObjectIconAndTitlePanel(
             final String id,
+            final IconWhere iconWhere,
             final ObjectAdapterModel objectAdapterModel) {
         super(id, objectAdapterModel);
+        this.iconWhere = iconWhere;
         guardAgainstNonEmptyAbstractSingular(objectAdapterModel);
     }
 
@@ -75,13 +80,13 @@ protected ManagedObject linkedDomainObject() {
 
     @Override
     protected void onBeforeRender() {
-        buildGui();
+        buildGui(iconWhere);
         super.onBeforeRender();
     }
 
     @Override
-    public ObjectIcon getIcon() {
-        return linkedDomainObject().getIcon();
+    public ObjectIcon getIcon(IconWhere iconWhere) {
+        return linkedDomainObject().getIcon(iconWhere);
     }
 
     /**
@@ -91,19 +96,19 @@ protected void onLinkWrapperCreated(final MarkupContainer 
linkWrapper) {}
 
     // -- HELPER
 
-    private void buildGui() {
-        addLinkWrapper();
+    private void buildGui(IconWhere iconWhere) {
+        addLinkWrapper(iconWhere);
         setOutputMarkupId(true);
     }
 
-    private void addLinkWrapper() {
+    private void addLinkWrapper(IconWhere iconWhere) {
         var linkWrapper = Wkt.container(ID_OBJECT_LINK_WRAPPER);
-        linkWrapper.addOrReplace(createLinkWithIconAndTitle());
+        linkWrapper.addOrReplace(createLinkWithIconAndTitle(iconWhere));
         addOrReplace(linkWrapper);
         onLinkWrapperCreated(linkWrapper);
     }
 
-    private AbstractLink createLinkWithIconAndTitle() {
+    private AbstractLink createLinkWithIconAndTitle(IconWhere iconWhere) {
         final ManagedObject linkedDomainObject = linkedDomainObject();
         final AbstractLink link = 
createDynamicallyVisibleLink(linkedDomainObject);
 
@@ -116,6 +121,7 @@ private AbstractLink createLinkWithIconAndTitle() {
             Wkt.labelAdd(link, ID_OBJECT_TITLE, titleAbbreviated("(no 
object)"));
         } else {
             HasIcon.super.visitIconVariant(
+                iconWhere,
                 iconUrlBased->{
                     Wkt.imageAddCachable(link, ID_OBJECT_ICON,
                         
getImageResourceCache().resourceReferenceForObjectIcon(iconUrlBased));
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanelFactory.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanelFactory.java
index 854b710953d..a263b5c249d 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanelFactory.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanelFactory.java
@@ -21,6 +21,7 @@
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.viewer.commons.model.components.UiComponentType;
@@ -92,16 +93,17 @@ public Component createComponent(final String id, final 
IModel<?> model) {
             throw _Exceptions.unexpectedCodeReach();
         }
 
-        return new ObjectIconAndTitlePanel(id, objectAdapterModel);
+        return new ObjectIconAndTitlePanel(id, IconWhere.TABLE_ROW, 
objectAdapterModel);
     }
 
     /**
      * refactoring hint: go through proper ComponentFactory channels instead
      */
+    @Deprecated
     public static Component entityIconAndTitlePanel(
             final String componentId,
             final ObjectAdapterModel objectAdapterModel) {
-        return new ObjectIconAndTitlePanel(componentId, objectAdapterModel);
+        return new ObjectIconAndTitlePanel(componentId, IconWhere.TABLE_ROW, 
objectAdapterModel);
     }
 
 }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanel.java
index 87bb1324c4a..1c545901299 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanel.java
@@ -20,12 +20,13 @@
 
 import org.apache.wicket.MarkupContainer;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
 import 
org.apache.causeway.viewer.wicket.ui.components.widgets.zclip.ZeroClipboardPanel;
 
 /**
  * An extension of {@link 
org.apache.causeway.viewer.wicket.ui.components.object.icontitle.ObjectIconAndTitlePanel}
- * that additionally has a link allowing to copy the url to the shown entity
+ * that additionally has a link allowing to copy the URL to the shown entity
  */
 class ObjectIconTitleAndCopyLinkPanel extends ObjectIconAndTitlePanel {
 
@@ -33,8 +34,8 @@ class ObjectIconTitleAndCopyLinkPanel extends 
ObjectIconAndTitlePanel {
 
     private static final String ID_COPY_LINK = "copyLink";
 
-    public ObjectIconTitleAndCopyLinkPanel(final String id, final UiObjectWkt 
objectModel) {
-        super(id, objectModel);
+    public ObjectIconTitleAndCopyLinkPanel(final String id, final IconWhere 
iconWhere, final UiObjectWkt objectModel) {
+        super(id, iconWhere, objectModel);
     }
 
     @Override
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanelFactory.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanelFactory.java
index fa2921f3b3a..0331be1c539 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanelFactory.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconTitleAndCopyLinkPanelFactory.java
@@ -21,6 +21,7 @@
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
+import org.apache.causeway.applib.annotation.ObjectSupport.IconWhere;
 import org.apache.causeway.viewer.commons.model.components.UiComponentType;
 import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
 import 
org.apache.causeway.viewer.wicket.ui.components.object.ObjectComponentFactoryAbstract;
@@ -38,6 +39,6 @@ public ObjectIconTitleAndCopyLinkPanelFactory() {
     @Override
     public Component createComponent(final String id, final IModel<?> model) {
         final UiObjectWkt objectModel = (UiObjectWkt) model;
-        return new ObjectIconTitleAndCopyLinkPanel(id, objectModel);
+        return new ObjectIconTitleAndCopyLinkPanel(id, 
IconWhere.OBJECT_HEADER, objectModel);
     }
 }

Reply via email to