[[object-titles-and-icons]]
= Titles, Icons etc.

:Notice: 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.
:page-partial:


In Apache Isis every object is identified to the user by a title (label) and an icon.
This is shown in several places: as the main heading for an object; as a link text for an object referencing another object, and also in tables representing collections of objects.

The icon is often the same for all instances of a particular class, but it's also possible for an individual instance to return a custom icon.
This could represent the state of that object (eg a shipped order, say, or overdue library book).

It is also possible for an object to provide a CSS class hint.
In conjunction with xref:vw:ROOT:customisation.adoc#custom-css[customized CSS] this can be used to apply arbitrary styling; for example each object could be rendered in a page with a different background colour.

Finally, a domain object can even indicate the xref:ui.adoc#object-layout[layout] that should be used to render it.
For example, a domain object may be rendered differently depending upon the role of the user viewing it.

[#object-title]
== Object Title

Generally the object title is a label to identify an object to the end-user.
There is no requirement for it to be absolutely unique, but it should be "unique enough" to distinguish the object from other object's likely to be rendered on the same page.

The title is always shown with an icon, so there is generally no need for the title to include information about the object's type.
For example the title of a customer object shouldn't include the literal string "Customer"; it can just have the customer's name, reference or some other meaningful business identifier.

=== Declarative style

The xref:refguide:applib:index/annotation/Title.adoc[@Title] annotation can be used build up the title of an object from its constituent parts.

For example:

[source,java]
----
import lombok.Getter;

public class Customer {

    @Title(sequence="1", append=" ")    // <.>
    @Getter
    private String firstName;

    @Title(sequence="2")                // <.>
    @Getter
    private String lastName;

    // ...
}
----
<.> First component of the title, with a space afterwards
<.> Second component of the title

might return "Arthur Clarke", while:

[source,java]
----
import lombok.Getter;

public class CustomerAlt {

    @Title(sequence="2", prepend=", ")
    @Getter
    private String firstName;

    @Title(sequence="1")
    @Getter
    private String lastName;

    // ...
}
----

could return "Clarke, Arthur".

Note that the sequence is in Dewey Decimal Format, which allows a subclass to intersperse information within the title.
For example:

[source,java]
----
import lombok.Getter;

public class Author extends Customer {

    @Title(sequence="1.5", append=". ") // <.>
    @Getter
    private String middleInitial;

    // ...
}
----
<.> "Slots" between the components defined by the superclass

could return "Arthur C. Clarke".

=== Imperative style

Alternatively, the title can be provided simply by implementing the xref:refguide:applib-methods:ui-hints.adoc#title[title()] reserved method.

For example:

[source,java]
----
public class Author extends Customer {

    public String title() {
        final StringBuilder buf = new StringBuilder();
        buf.append(getFirstName());
        if(getMiddleInitial() != null) {
            buf.append(getMiddleInitial()).append(". ");
        }
        buf.append(getLastName();
        return buf.toString();
    }

    ...
}
----

A variation on this approach also supports localized names; see xref:userguide:btb:i18n.adoc[beyond-the-basics] guide for further details.

=== Using a UI subscriber

A third alternative is to move the responsibility for deriving the title into a separate subscriber object.

In the target object, we define an appropriate event type and use the xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#titleUiEvent[@DomainObjectLayout#titleUiEvent()] attribute to specify:

[source,java]
----
@DomainObjectLayout(
    titleUiEvent = Author.TitleUiEvent.class
)
public class Author extends Customer {
    public static class TitleUiEvent
            extends org.apache.isis.applib.events.ui.TitleUiEvent<Author> {}

    //...
}
----

The subscriber can then populate this event:

[source,java]
----
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;

@Service
public class AuthorSubscriptions {

    @EventListener(Author.TitleUiEvent.class)
    public void on(Author.TitleUiEvent ev) {
        val author = ev.getSource();
        ev.setTitle(titleOf(author));
    }

    private String titleOf(Author author) {
        val buf = new StringBuilder();
        buf.append(author.getFirstName());
        if(author.getMiddleInitial() != null) {
            buf.append(author.getMiddleInitial()).append(". ");
        }
        buf.append(author.getLastName());
        return buf.toString();
    }
}
----

UI listeners are useful when using third-party libraries or extensions.

[#object-icon]
== Object Icon

The icon is often the same for all instances of a particular class, and is picked up by convention.

It's also possible for an individual instance to return a custom icon, typically so that some significant state of that domain object is represented.
For example, a custom icon could be used to represent a shipped order, say, or an overdue library loan.

=== Declarative style

If there is no requirement to customize the icon (the normal case), then the icon is usually picked up as the `.png` file in the same package as the class.
For example, the icon for a class `org.mydomain.myapp.Customer` will be `org/mydomain/myapp/Customer.png` (if it exists).

Alternatively, a font-awesome icon can be used.
This is specified using the xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#cssClassFa[@DomainObjectLayout#cssClassFa()] attribute or in the xref:userguide:fun:ui.adoc#object-layout[layout.xml] file.

For example:

[source,java]
----
@DomainObjectLayout( cssClassFa="play" )    // <1>
public class InvoiceRun {
    ...
}
----
<1> will use the "fa-play" icon.

=== Imperative style

To customise the icon on an instance-by-instance basis, we implement the reserved xref:refguide:applib-methods:ui-hints.adoc#iconName[iconName()] method.

For example:

[source,java]
----
public class Order {
    public String iconName() {
        return isShipped() ? "shipped": null;
    }
    // ..
}
----

In this case, if the `Order` has shipped then the framework will look for an icon image named "Order-shipped.png" (in the same package as the class).
Otherwise it will just use "Order.png", as normal.

=== Using a UI subscriber

As for title, the determination of which image file to use for the icon can be externalized into a UI event subscriber.

In the target object, we define an appropriate event type and use the xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#iconUiEvent[@DomainObjectLayout#iconUiEvent()] attribute to specify.

For example:

[source,java]
----
@DomainObjectLayout(
    iconUiEvent = Order.IconUiEvent.class
)
public class Order {
    public static class IconUiEvent
            extends org.apache.isis.applib.events.ui.IconUiEvent<Order> {}
    // ..
}
----

The subscriber can then populate this event:

[source,java]
----
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;

@Service
public class OrderSubscriptions {

    @EventListener(Order.IconUiEvent.class)
    public void on(Order.IconUiEvent ev) {
        val order = ev.getSource();
        ev.setIconName(iconNameOf(order);
    }

    private String iconNameOf(Order order) {
        return order.isShipped() ? "shipped": null;
    }
}
----

== Object CSS Styling

It is also possible for an object to return a xref:refguide:applib-methods:ui-hints.adoc#cssClass[CSS class].
In conjunction with a viewer-specific customisation of CSS (eg for the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], see xref:vw:ROOT:customisation.adoc#custom-css[here]) this can be used to apply arbitrary styling; for example each object could be rendered in a page with a different background colour.

=== Declarative style

To render an object with a particular CSS, use
xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#cssClass[@DomainObjectLayout#cssClass()] or in the xref:userguide:fun:ui.adoc#object-layout[layout.xml] file.

The usage of this CSS class is viewer-specific.
In the case of the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], when the domain object is rendered on its own page, this CSS class will appear on a top-level `<div>`.
Or, when the domain object is rendered as a row in a collection, then the CSS class will appear in a `<div>` wrapped by the `<tr>` of the row.

One possible use case would be to render the most important object types with a subtle background colour: ``Customer``s shown in light green, or ``Order``s shown in a light pink, for example.

=== Imperative style

To specify a CSS class on an instance-by-instance basis, we implement the reserved xref:refguide:applib-methods:ui-hints.adoc#cssClass[cssClass()] method.

For example:

[source,java]
----
public class Order {
    public String cssClass() {
        return isShipped() ? "shipped": null;       <.>
    }
    ...
}
----
<.> the implementation might well be the same as the `iconName()`.

If non-null value is returned then the CSS class will be rendered _in addition_ to any declarative CSS class also specified.

=== Using a UI subscriber

As for title and icon, the determination of which CSS class to render can be externalized into a UI event subscriber.

In the target object, we define an appropriate event type and use the xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#cssClassUiEvent[@DomainObjectLayout#cssClassUiEvent()] attribute to specify.

For example

[source,java]
----
@DomainObjectLayout( cssClassUiEvent = Order.CssClassUiEvent.class )
public class Order {
    public static class CssClassUiEvent
            extends org.apache.isis.applib.events.ui.CssClassUiEvent<Order> {}
    // ..
}
----

The subscriber can then populate this event:

[source,java]
----
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;

@Service
public class OrderSubscriptions {

    @EventListener(Order.CssClassUiEvent.class)
    public void on(Order.CssClassUiEvent ev) {
        val order = ev.getSource();
        ev.setCssClass(cssClassOf(order));
    }

    private static String cssClassOf(Order order) {
        return order.isShipped() ? "shipped": null;
    }
}
----



[#switching-layouts]
== Switching Layouts

A domain object may also have multiple layouts.
One reason might be based on the role of the user viewing the object; the object members most relevant to a data entry clerk could be quite different to an manager that is viewing, eg to approve it.
The layout could be used to hide some object members, show others.

If an alternative layout is indicated (we'll look at the mechanics of this below), then this is used to locate an alternative layout file.
For example, if the "edit" layout is specified, then the `Xxx.edit.layout.xml` file is used (if it exists).

More generally, for a given domain object `Xxx`, if it has specified a layout `yyy`, then the framework will search for a file `Xxx.yyy.layout.xml` on the classpath.


=== Imperative style

To specify the layout on an instance-by-instance basis, we implement the reserved xref:refguide:applib-methods:ui-hints.adoc#layout[layout()] method.

For example:

[source,java]
----
public class IncomingInvoice {
    public String layout() {
        return isUserInDataEntryRole() ? "edit": null;
    }
    ...
}
----

=== Using a UI subscriber

As for title, icon and CSS, the determination of which layout class to render can be externalized into a UI event subscriber.

In the target object, we define an appropriate event type and use the xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#layoutUiEvent[@DomainObjectLayout#layoutUiEvent()] attribute to specify.

For example

[source,java]
----
@DomainObjectLayout( layoutUiEvent = Order.LayoutUiEvent.class )
public class IncomingInvoice {
    public static class LayoutUiEvent
            extends org.apache.isis.applib.events.ui.LayoutUiEvent<Order> {}
    // ..
}
----

The subscriber can then populate this event:

[source,java]
----
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;

@Service
public class IncomingInvoiceSubscriptions {

    @EventListener(IncomingInvoice.LayoutUiEvent.class)
    public void on(IncomingInvoice.LayoutUiEvent ev) {
        val incomingInvoice = ev.getSource();
        ev.setLayout(layoutOf(incomingInvoice));
    }

    private static String layoutOf(IncomingInvoice incomingInvoice) {
        return isUserInDataEntryRole() ? "edit": null;
    }
}
----

=== Fallback layouts

In addition to searching for alternate layouts and then the default layout, in the absence of either the framework will also search for a "fallback" layouts.

The use case is to allow libraries that provide domain objects (for example, the xref:security:secman:about.adoc[SecMan] extension) to define the UI of these objects using a layout file, while still allowing the consuming application to override that layout if it so requires.

Thus, for a domain object "Xxx", the framework searches:

. _Xxx.yyy.layout.xml_
+
for layout "yyy" (if a non-null layout "yyy" is specified)

. _Xxx.layout.xml_
+
the default layout

. _Xxx.layout.fallback.xml_
+
the fallback layout

If none of these exist, then the framework will use a layout based on any available annotations.
The page will be split 4:8, with the first column for properties and the second column for collections.

