Class MainWindowRoute

java.lang.Object
com.vaadin.flow.component.Component
com.vaadin.flow.component.HtmlComponent
com.vaadin.flow.component.HtmlContainer
com.vaadin.flow.component.html.Div
vaadinx.swing.app.MainWindowRoute
All Implemented Interfaces:
com.vaadin.flow.component.AttachNotifier, com.vaadin.flow.component.ClickNotifier<com.vaadin.flow.component.html.Div>, com.vaadin.flow.component.DetachNotifier, com.vaadin.flow.component.HasComponents, com.vaadin.flow.component.HasComponentsOfType<com.vaadin.flow.component.Component>, com.vaadin.flow.component.HasElement, com.vaadin.flow.component.HasEnabled, com.vaadin.flow.component.HasOrderedComponents, com.vaadin.flow.component.HasSize, com.vaadin.flow.component.HasStyle, com.vaadin.flow.component.HasText, Serializable

public abstract class MainWindowRoute extends com.vaadin.flow.component.html.Div
Base class for the Vaadin route that hosts a migrated app's main JFrame. Migrators paste a one-class scaffold extending this base into their app, owning @Route / @PreserveOnRefresh / the bootstrap call:
@Route("") @PreserveOnRefresh
public class AppRoute extends MainWindowRoute {
    @Override protected void bootstrap() { MyApp.mainUI(); }
}

Attaching throws IllegalStateException when SwingBridgeEmulatorsBootstrap never ran for the attaching UI — the app forgot the one SPI line, and the route is the earliest place that is knowable (M1D_bootstrap_canonical).

bootstrap() runs at most once per instance. On every attach (initial + @PreserveOnRefresh re-bind to a fresh UI), the route registers itself as the current MainWindowRoute on the attaching UI via ComponentUtil.setData(Component, SerializableTriConsumer, T, U), so the @MainWindow-annotated JFrame constructed inside (or transitively, via SwingUtilities.invokeLater + EHelper.callSwing) captures the route as its inline-render target. Subsequent onAttach calls on the same instance skip bootstrap(); the user's preserved component subtree (including the @MainWindow JFrame's peer) is reused.

UI-scoped data, not ThreadLocal

The route→UI binding lives on UI.getCurrent() (ComponentUtil.setData(Component, SerializableTriConsumer, T, U)), not a ThreadLocal around bootstrap(), because the canonical Swing entry wraps frame construction in SwingUtilities.invokeLater(...): that runs through EHelper.callSwing's virtual-thread executor, so the frame ctor fires on a VT continuation after bootstrap() has returned (a bootstrap-scoped ThreadLocal would already be cleared) and on a fresh thread with its own thread-local table (so it'd read null anyway). UI-scoped data survives both the synchronous and VT-deferred hop because callSwing guarantees UI.getCurrent() is the calling thread's UI (R_callswing_envelope / D_callswing_loom).

Why an instance flag and not AttachEvent#isInitialAttach

Flow's isInitialAttach() returns true on every @PreserveOnRefresh re-bind (the detached node's id resets to -1, so the rebind reads as initial), so gating bootstrap() on it would construct a fresh @MainWindow frame on every browser refresh and stack it under the preserved Div. The bootstrapped instance flag is the reliable gate — @PreserveOnRefresh preserves the instance, so an instance field is exactly once-per-instance.

App-tab identity & multi-tab (option 1)

onAttach maintains the session's AppTab — the one UI (and browser tab) the app lives in — via AppTab.markAppUI(UI). The same bootstrapped flag that gates bootstrap() doubles as the F5-vs-new-tab discriminator: a preserved-instance re-attach (F5) re-binds the fresh UI to the app tab; a fresh instance while the app is already live in another UI is a second tab, which renders onAppAlreadyActive() instead of running a second copy (one app per session). EHelper.singleLiveUI(VaadinSession) resolves the AppTab first. See D_active_ui_pointer.

Per D_mainwindow_route: lifecycle plumbing (once-per-instance bootstrap gating, UI-scoped route registration, eventual session-destroy → WINDOW_CLOSING listener) lives here so we can evolve it without touching every migrated app.

See Also:
  • Nested Class Summary

    Nested classes/interfaces inherited from interface com.vaadin.flow.component.HasText

    com.vaadin.flow.component.HasText.WhiteSpace
  • Constructor Summary

    Constructors
    Constructor
    Description
    Default sizing: fill the browser viewport.
  • Method Summary

    Modifier and Type
    Method
    Description
    protected abstract void
    Invoked once per route instance, on first attach.
    Read by JFrame's ctor to bind a @MainWindow frame to the route registered on the current UI.
    protected void
    Rendered instead of the app when this UI is a second tab and the app is already active in another UI (multi-tab option 1: one app per session, non-active tabs don't run it).
    protected void
    onAttach(com.vaadin.flow.component.AttachEvent attachEvent)
     
    protected void
    onDetach(com.vaadin.flow.component.DetachEvent detachEvent)
     

    Methods inherited from class com.vaadin.flow.component.HtmlComponent

    getTitle, setTitle

    Methods inherited from class com.vaadin.flow.component.Component

    addListener, bindVisible, findAncestor, fireEvent, from, get, getChildren, getElement, getEventBus, getId, getListeners, getLocale, getParent, getTestId, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getUI, hasListener, isAttached, isTemplateMapped, isVisible, onEnabledStateChanged, removeFromParent, scrollIntoView, scrollIntoView, set, setElement, setId, setTestId, setVisible, whenAttached

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods inherited from interface com.vaadin.flow.component.AttachNotifier

    addAttachListener

    Methods inherited from interface com.vaadin.flow.component.ClickNotifier

    addClickListener, addClickShortcut, addDoubleClickListener, addSingleClickListener

    Methods inherited from interface com.vaadin.flow.component.DetachNotifier

    addDetachListener

    Methods inherited from interface com.vaadin.flow.component.HasComponents

    add

    Methods inherited from interface com.vaadin.flow.component.HasComponentsOfType

    add, add, addComponentAsFirst, addComponentAtIndex, bindChildren, getChildren, getComponentAt, getComponentCount, indexOf, remove, remove, removeAll, replace

    Methods inherited from interface com.vaadin.flow.component.HasElement

    getElement

    Methods inherited from interface com.vaadin.flow.component.HasEnabled

    bindEnabled, isEnabled, setEnabled

    Methods inherited from interface com.vaadin.flow.component.HasSize

    bindHeight, bindWidth, getHeight, getHeightUnit, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getWidth, getWidthUnit, setHeight, setHeight, setHeightFull, setMaxHeight, setMaxHeight, setMaxWidth, setMaxWidth, setMinHeight, setMinHeight, setMinWidth, setMinWidth, setSizeFull, setSizeUndefined, setWidth, setWidth, setWidthFull

    Methods inherited from interface com.vaadin.flow.component.HasStyle

    addClassName, addClassNames, bindClassName, bindClassNames, getClassName, getClassNames, getStyle, hasClassName, removeClassName, removeClassNames, setClassName, setClassName

    Methods inherited from interface com.vaadin.flow.component.HasText

    bindText, getText, getWhiteSpace, setText, setWhiteSpace
  • Constructor Details

    • MainWindowRoute

      public MainWindowRoute()
      Default sizing: fill the browser viewport. Per D_inline_route_sizing, a @MainWindow JFrame's route fills the viewport unless the user calls pack() — main windows occupy as much space as possible, opposed to dialogs that wrap their children. The ctor sets setSizeFull() so the default-fill mode lights up before any JFrame ctor runs; height-stretchable children (Vaadin Grid inside SJScrollPane, future TabPane / SplitPane) get a real height to expand into.

      JFrame.pack() on InlineStrategy clears this — see InlineStrategy.applyInlineSizingOnPack.

  • Method Details

    • current

      public static MainWindowRoute current()
      Read by JFrame's ctor to bind a @MainWindow frame to the route registered on the current UI. Returns null if there is no current UI or no MainWindowRoute is mounted on it.
    • onAttach

      protected void onAttach(com.vaadin.flow.component.AttachEvent attachEvent)
      Overrides:
      onAttach in class com.vaadin.flow.component.Component
    • onAppAlreadyActive

      protected void onAppAlreadyActive()
      Rendered instead of the app when this UI is a second tab and the app is already active in another UI (multi-tab option 1: one app per session, non-active tabs don't run it). Default shows a plain message; override to customise (e.g. a "reload to take over here" action once the active tab is gone — a future option-2/3 upgrade). Runs under the session lock on the attaching UI.
    • onDetach

      protected void onDetach(com.vaadin.flow.component.DetachEvent detachEvent)
      Overrides:
      onDetach in class com.vaadin.flow.component.Component
    • bootstrap

      protected abstract void bootstrap()
      Invoked once per route instance, on first attach. Implementations typically call the migrated app's mainUI() method, which instantiates the @MainWindow JFrame and calls setVisible(true). Wrapping construction in SwingUtilities.invokeLater(...) is supported — the route's registration on the current UI survives the VT hop.