# Overview

MicroEJ Add-on Library: `Navigation Framework`.

This library structures multi-screen MicroEJ applications around a stack-based navigation model.
Navigation is **by page key**: the application calls a navigation operation with the `int` key of the
screen to show, and a single page factory builds the corresponding page. The application never hands a
page instance to the framework. A single `Navigator` owns the navigation history, performs the
navigation operations, animates each page change with a transition, delivers page lifecycle callbacks,
and reports navigation events to registered `NavigationListener` instances.

The main concepts are:

- **`Page`**: the unit of navigation. An application extends it and implements `getContent()` to
  build the screen's visual content, which is rebuilt each time the page is shown. The page is also
  notified when it is entered and exited through the `onEntered()` and `onExited()` lifecycle
  callbacks. A page receives its per-navigation data through its own constructor, called by the
  factory; the framework owns no parameter slot.
- **`PageFactory`**: the single component that maps a page key to a fresh `Page`, through
  `create(int, Object)`. The application implements it once, typically as one `switch` over its key
  constants, and rejects an unknown key with `IllegalArgumentException`. Along with the key it receives
  the argument the navigation carried, and injects it into the page's own constructor. It is consulted
  only by forward navigation; back navigation re-shows the page instance held in the history.
- **`Navigator`**: the controller and entry point, and a singleton. The application initializes it
  once with the MicroUI `Desktop` it drives and the factory
  (`Navigator.initialize(Desktop, PageFactory)`), then obtains it with `Navigator.getInstance()`. It
  exposes the navigation operations (`navigateTo(int)`, `navigateBack()`, `navigateBackTo(int)`,
  `replaceWith(int)`, each with a transition overload, and the two page-building ones also with an
  argument-carrying overload), the history queries (`getActivePage`,
  `getActiveKey`, `getHistory`, `getHistoryKeys`), the default transition (`setTransition`), and the
  listener registration. The first navigation shows the desktop and makes its page the root at the
  bottom of the history. Any method called before initialization throws `IllegalStateException`.
- **`Transition`**: animates the change from the outgoing content to the incoming one. It and the
  shipped transitions live in the `ej.navigation.transition` sub-package: `Transition.IMMEDIATE` (the
  instant, non-animated default), `FadeTransition`, and `SlideTransition`. An application may supply
  its own, rendering through the `ej.navigation.transition.TransitionContext` it receives and signaling
  completion through a `TransitionListener`. Transitions are parameterized objects, not enum
  constants.
- **`NavigationListener`**: an external observer notified once per navigation, of the outgoing and the
  incoming page.

Two rules shape the model. **Identity is per history entry**: the same key may appear more than once
in the history, and every forward navigation asks the factory for a fresh page, so a parameterized
drill-down such as *list → detail(A) → detail(B)* needs nothing special. And **`replaceWith` discards
the page it replaces**: it swaps the active page in place without growing the history, so the next
`navigateBack()` reveals the entry beneath the replacing one.

# Usage

Add the following line to your `build.gradle.kts`:

```kotlin
implementation("ej.library.ui:navigation-framework:1.0.1")
```

Name each screen with an `int` constant (no enums):

```java
public final class Pages {

	public static final int HOME = 0;
	public static final int DETAILS = 1;

	private Pages() {
		// Constants holder: not instantiable.
	}
}
```

Write each screen as a `Page`, taking its per-navigation data through its constructor and navigating
through the navigator:

```java
public final class DetailsPage extends Page {

	private final Item item;

	public DetailsPage(Item item) {
		this.item = item;
	}

	@Override
	protected Widget getContent() {
		// Build and return the screen's MWT widget tree. A "Back" button calls
		// Navigator.getInstance().navigateBack().
	}
}
```

Map the keys to the pages in a single factory:

```java
public final class AppPageFactory implements PageFactory {

	@Override
	public Page create(int key, @Nullable Object argument) {
		switch (key) {
		case Pages.HOME:
			return new HomePage();
		case Pages.DETAILS:
			return new DetailsPage((Item) argument);
		default:
			throw new IllegalArgumentException();
		}
	}
}
```

A minimal navigation flow:

```java
MicroUI.start();
Desktop desktop = new Desktop();
desktop.setStylesheet(createStylesheet());          // the application owns the desktop and its styling

Navigator.initialize(desktop, new AppPageFactory());
Navigator navigator = Navigator.getInstance();
navigator.setTransition(new SlideTransition());     // default for the no-transition overloads

navigator.navigateTo(Pages.HOME);                   // shows the desktop; HOME becomes the root
navigator.navigateTo(Pages.DETAILS, selectedItem);  // the argument reaches DetailsPage's constructor
navigator.navigateBack();                           // back to HOME
navigator.replaceWith(Pages.DETAILS, otherItem, new FadeTransition());
```

MicroUI must be started before the first navigation, and every navigator method must be called from
the MicroUI thread. A navigation requested from a lifecycle callback or a listener callback is not run
inline: it is deferred with `MicroUI.callSerially()` and performed once the navigation in progress has
been dispatched. If that navigation's transition is still animating, it is snapped to its end first.

# Requirements

This library requires the following Foundation Libraries:

| Foundation Library | Version |
| ------------------ | ------- |
| EDC                | 1.3     |
| MicroUI            | 3.6     |

# Dependencies

_All dependencies are retrieved transitively by Gradle_.

| Add-On Library | Version |
| -------------- | ------- |
| MWT            | 3.7     |

# Source

N/A.

# Restrictions

- **The navigator is a singleton**: one navigator, driving one `Desktop`, per application. It must be
  initialized before any other call, and initializing it again restarts it from a clean state (empty
  history, no listener, instant default transition).
- **A navigation carries at most one argument, and the framework does not type it.** It is handed as
  an `Object` to `PageFactory.create(int, Object)`, which is responsible for checking what it received:
  the navigator neither inspects the argument nor matches it against the key. It reaches the page
  through the page's own constructor; the framework keeps no reference to it and imposes no parameter
  slot on `Page`.
- **Back navigation carries no argument.** `navigateBack()` and `navigateBackTo(int)` re-show the page
  instance the history holds and never consult the factory.
- **The framework never accepts a page instance**, and the pages it builds are not reachable for
  reuse: forward navigation always builds a fresh page. Back navigation re-shows the page instance the
  history holds, so a page's fields survive a round trip, but its content widget is rebuilt on every
  show.
- **`FadeTransition` cross-dissolves from a snapshot of the incoming content**, so content that
  animates itself is frozen for the duration of the fade, and the fade allocates a buffer the size of
  the content area while it runs. Prefer `SlideTransition` or `Transition.IMMEDIATE` where that cost
  matters.
- Out of scope in this version: retaining a page's content across navigations, a
  "can I navigate back?" query, a navigator-scoped context shared by every page, and popups or
  overlays.

_Copyright 2026 MicroEJ Corp. All rights reserved._\
_This library is provided in source code for use, modification and test, subject to license terms._\
_Any modification of the source code will break MicroEJ Corp. warranties on the whole library._\
_Build: 7E4D1F7C_
