pub trait ResourceApp: 'static {
type Resource: Resource;
Show 18 associated constants and 20 methods
const ID: &'static str;
const TITLE: &'static str;
const ICON: Option<&'static str> = None;
const BEHAVIOUR: ComponentBehaviour = ComponentBehaviour::DEFAULT;
const OPENS: &'static [&'static str] = _;
const ARGUMENTS: usize = 0;
const DOCUMENT_HISTORY: bool = true;
const WORKSPACE_RECORD: bool = false;
const WINDOW_RESOURCE: bool = false;
const OPTIONS: &'static [&'static str] = _;
const FLAGS: &'static [&'static str] = _;
const PRIVATE_WINDOWS: bool = false;
const FILE_HISTORY: Option<FileHistory> = None;
const READS_HOST: &'static [HostSource] = _;
const OPENS_BEFORE_RECORD: bool = false;
const ATTACHES_RUNTIME: bool = true;
const HOSTED_BY_SESSION: bool = false;
const PERSON_RECORDS: &'static [PersonKind] = _;
// Required method
fn view(cx: &mut ViewCx<Self::Resource>) -> Element;
// Provided methods
fn named() -> Option<AppName> { ... }
fn member_record() -> bool { ... }
fn import_record(context: &HostContext) -> Result<(), String> { ... }
fn opening(cx: &OpeningCx<'_>) -> Option<Box<dyn OpeningView>> { ... }
fn label(state: &<Self::Resource as Resource>::State) -> String { ... }
fn local_operations() -> Vec<ActionSpec> { ... }
fn launch_record(input: &LaunchInput<'_>) -> Option<RecordId> { ... }
fn launch(
input: &LaunchInput<'_>,
created: bool,
) -> Vec<Invoke<Self::Resource>> { ... }
fn genesis(input: &LaunchInput<'_>) -> Vec<Invoke<Self::Resource>> { ... }
fn commands() -> &'static [Command] { ... }
fn helpers() -> &'static [Helper] { ... }
fn catalog() -> Option<&'static EmbeddedCatalog> { ... }
fn scenarios() -> &'static [&'static str] { ... }
fn shot_viewport(scenario: Option<&str>) -> (u32, u32) { ... }
fn fixture(fixture: &mut Fixture<Self::Resource>) { ... }
fn scenario(name: Option<&str>) -> Vec<Invoke<Self::Resource>> { ... }
fn scenario_input(
name: Option<&str>,
input: &mut FrameHarness,
) -> Result<(), String> { ... }
fn seed(cx: &SeedCx<'_>) -> Vec<Invoke<Self::Resource>> { ... }
fn records(records: &mut Records<'_>) { ... }
}Expand description
A framework app: one resource type and a view of it.
Everything else — storage, sync, collaboration, history, undo, permissions, the action palette, the agent and script door, search — is provided. See the module docs.
Required Associated Constants§
Provided Associated Constants§
Sourceconst BEHAVIOUR: ComponentBehaviour = ComponentBehaviour::DEFAULT
const BEHAVIOUR: ComponentBehaviour = ComponentBehaviour::DEFAULT
How every window hosting the app treats it: focus after a rebuild,
live appearance, and whether captures withhold it. An app that shows
secrets declares capture_protected here, so the live launch, the
shell and every other host of its component protect it alike; an
on-screen keyboard declares its window an
input surface.
Sourceconst OPENS: &'static [&'static str] = _
const OPENS: &'static [&'static str] = _
Media types (type/subtype, lowercase) of the files the app opens
when a host hands it one (a file association, a file dropped on its
icon, a path of that type on its command line); Self::launch
receives the file. "*/*"
(ANY_MEDIA_TYPE) opens every
file, a file whose name says nothing of its type as
application/octet-stream.
Sourceconst ARGUMENTS: usize = 0
const ARGUMENTS: usize = 0
How many arguments (paths, URLs) a launch may name;
Self::launch_record and Self::launch receive them.
Sourceconst DOCUMENT_HISTORY: bool = true
const DOCUMENT_HISTORY: bool = true
Whether the resource keeps document state whose versions History
shows. An app whose state lives outside its document (a measurement,
a device) sets false: it publishes no History actions and its
window offers no History.
Sourceconst WORKSPACE_RECORD: bool = false
const WORKSPACE_RECORD: bool = false
The app brings what its windows kept before it kept one record per
workspace into that record in its own seed
(SeedCx::window_records), as Notes copies its pages by id. Every
app keeps one record per workspace, which every window of it there
opens and the first one creates; without this the framework copies
the earlier windows’ records into it value by value
(adopt_windows).
Sourceconst WINDOW_RESOURCE: bool = false
const WINDOW_RESOURCE: bool = false
Each window shows a resource of its own: a document an editor opened
(Word Processor, Code Editor, Sheets), a terminal session. A window
that names none starts a new one, and two windows show two of them
(or one document together, when both open it); no window mirrors
another. The documents earlier windows started stay in the workspace:
the app lists them with RecordEnv::window_documents
and opens one in a window with RecordEnv::open_here.
Without it the app’s record is its library and settings,
one per workspace, which every window of the app there shows.
Sourceconst OPTIONS: &'static [&'static str] = _
const OPTIONS: &'static [&'static str] = _
The app’s own command-line options that take a value
(--purpose number or --purpose=number): how this window runs
rather than what it opens. Self::launch reads them from its
LaunchInput::option, views and surfaces from
RecordEnv::launch_option.
Sourceconst FLAGS: &'static [&'static str] = _
const FLAGS: &'static [&'static str] = _
The app’s own switches, which take no value (--debug-hit-areas):
LaunchInput::flag, RecordEnv::launch_flag.
Sourceconst PRIVATE_WINDOWS: bool = false
const PRIVATE_WINDOWS: bool = false
Whether the app opens private windows (--private): a window on a
profile of its own that is thrown away with the process — its record,
settings and files never reach the person’s — and that attaches to
no runtime, so nothing it shows syncs or is shared. It holds none of
the person’s own records (RecordEnv::person answers None), and its
view tells with RecordEnv::is_private.
Sourceconst FILE_HISTORY: Option<FileHistory> = None
const FILE_HISTORY: Option<FileHistory> = None
The history of a file of the mounted filesystem the app shows (the
file selected in a file manager), a History scope beside the
resource’s own: its versions are read, previewed and restored through
the filesystem’s projection, not the app’s document. The app’s
view names the file with RecordEnv::file_history.
Sourceconst READS_HOST: &'static [HostSource] = _
const READS_HOST: &'static [HostSource] = _
What of the machine it runs on the app shows (its journal, its processes, its drives, the person’s real files). A headless drive that captures it must then name one of its scenarios and is built from that scenario’s fixtures, so no picture of the app shows the machine of whoever took it.
Sourceconst OPENS_BEFORE_RECORD: bool = false
const OPENS_BEFORE_RECORD: bool = false
The window shows before the workspace’s record is read: an app that keeps
what its first frame shows beside the record (a tilolang app’s
index) opens a record this device holds off the frame, shows
Self::opening meanwhile, and builds Self::view once the record
is had. Only for an app that keeps one record per workspace.
Sourceconst ATTACHES_RUNTIME: bool = true
const ATTACHES_RUNTIME: bool = true
Whether the app takes part in the workspace runtime of the workspace it is launched in. An app that only runs on this device (an imager on a platform without the runtime) says no.
Sourceconst HOSTED_BY_SESSION: bool = false
const HOSTED_BY_SESSION: bool = false
Whether the session’s document host keeps this app’s record, so the
app’s process opens none: its views read the readings the session
publishes and submit actions over its one connection
(docs/architecture/apps.md §8 step 2, one-app-model.md §4). An
app the host serves says yes and also
attaches nothing: it joins no workspace of
its own and holds no key or transport.
A launch the session did not make (a device with the runtime compiled in, a capture, a test) keeps the record here, as before.
Sourceconst PERSON_RECORDS: &'static [PersonKind] = _
const PERSON_RECORDS: &'static [PersonKind] = _
The person’s own records the app keeps
(PersonKind), declared with the app rather than
with the window showing it: each is opened with the window, kept in
the person’s own scope instead of a workspace’s, and part of the
app’s committed surface whether or not a view has shown it.
const PERSON_RECORDS: &'static [PersonKind] =
&[PersonKind::shared::<accounts::person::PersonAccounts>()];Required Associated Types§
Required Methods§
Provided Methods§
Sourcefn named() -> Option<AppName>
fn named() -> Option<AppName>
The app this process is, when only the launch knows it: a runtime that runs a package
(a tilolang app) is the package’s app, with its id, title and icon, rather than
Self::ID, Self::TITLE and Self::ICON. Asked once per process, before the
definition is first made.
Sourcefn member_record() -> bool
fn member_record() -> bool
Whether each member of a workspace keeps the app’s record as their own: a window that
names no record opens the person’s own record of the workspace, which only their devices
read, instead of the workspace’s record of the app
(ApplicationDefinition::member_record). Asked once per process, before the definition
is first made.
Sourcefn import_record(context: &HostContext) -> Result<(), String>
fn import_record(context: &HostContext) -> Result<(), String>
Import a former document format using host-local custody, before typed projection or resource publication. Unlike schema evolution this may depend on device services. A pending or failed import must leave the document unchanged; returning an error withholds the resource. Reopening retries the import, so retain acknowledged writes across pending retries.
Sourcefn opening(cx: &OpeningCx<'_>) -> Option<Box<dyn OpeningView>>
fn opening(cx: &OpeningCx<'_>) -> Option<Box<dyn OpeningView>>
What the window shows while the workspace’s record is read off the frame
(Self::OPENS_BEFORE_RECORD); None waits for the record. What it
returns is handed to Self::view once the record is had, as a
service lent to the window (RecordEnv::take_lent):
the view keeps showing it rather than building it again. A write it
makes before then waits for the record (OpeningCx::record).
Sourcefn label(state: &<Self::Resource as Resource>::State) -> String
fn label(state: &<Self::Resource as Resource>::State) -> String
The label of the open resource in the palette and in history.
Sourcefn local_operations() -> Vec<ActionSpec>
fn local_operations() -> Vec<ActionSpec>
View and effect operations of this app’s presentation, published on
the same resource as Resource::local_operations. A shared resource
type (rich text) does not know about one app’s chrome. Document
mutations do not belong here. History does not either: it is the
resource’s own history, not a toggle beside it.
Sourcefn launch_record(input: &LaunchInput<'_>) -> Option<RecordId>
fn launch_record(input: &LaunchInput<'_>) -> Option<RecordId>
The record a launch naming input opens: the file’s own record
(RecordId::identify_file), a record derived from it
(RecordId::derived), a minted one. None, the default, opens the
record the app would open anyway: the one the window showed last, else
the workspace’s default. Asked by a launcher that picks the record (the
command line, a test host), before the record is opened, so it must
not depend on anything but input.
Sourcefn launch(input: &LaunchInput<'_>, created: bool) -> Vec<Invoke<Self::Resource>>
fn launch(input: &LaunchInput<'_>, created: bool) -> Vec<Invoke<Self::Resource>>
The actions a launch naming input takes once its record is open;
created when this launch created the record, so actions that fill
a new record (the source of a new project) are taken only then. They
go through the same door as every control: checked against the
participant’s permissions, attributed, undoable and in history. A
launch that shows something rather than changing the resource (a
link to one item, a file to look at before acting on it) invokes one
of the app’s effects (Invoke::effect) through the same door. A
capture launched on nothing takes none: its scenario
stages the record.
When the record was already there, LaunchInput::record reads it
as it is: a launch on a file can tell that the file changed since
the record last read or wrote it.
Sourcefn genesis(input: &LaunchInput<'_>) -> Vec<Invoke<Self::Resource>>
fn genesis(input: &LaunchInput<'_>) -> Vec<Invoke<Self::Resource>>
What a record a launch naming input creates starts with besides
the resource’s initialize: the content of
the file it was launched on. The actions are written into the
record’s genesis, so they must depend on nothing but input — two
devices that each create the record of one file converge on one
document only if both write the same genesis. They are nobody’s
edit: no history entry, no undo.
Sourcefn commands() -> &'static [Command]
fn commands() -> &'static [Command]
The commands the app’s binary runs instead of opening a window.
Sourcefn helpers() -> &'static [Helper]
fn helpers() -> &'static [Helper]
The helpers the app’s binary also is (Helper), asked in order
before the app starts.
Sourcefn catalog() -> Option<&'static EmbeddedCatalog>
fn catalog() -> Option<&'static EmbeddedCatalog>
The app’s message catalog, loaded before its view is built in every host that mounts the app, so the view’s lookups find its messages.
Sourcefn scenarios() -> &'static [&'static str]
fn scenarios() -> &'static [&'static str]
Named screenshot scenarios for --shots --scenario NAME; --shots-dir
captures every one.
Sourcefn shot_viewport(scenario: Option<&str>) -> (u32, u32)
fn shot_viewport(scenario: Option<&str>) -> (u32, u32)
The viewport a capture of scenario (None for a plain --shots)
is drawn at, unless --viewport names one: a narrow scenario’s
phone-sized window.
Sourcefn fixture(fixture: &mut Fixture<Self::Resource>)
fn fixture(fixture: &mut Fixture<Self::Resource>)
Prepare what a capture of fixture.scenario() shows that its actions
cannot stage, before the view is built: typed stand-ins for live
sources, scratch files, content-store blobs, other people’s versions
and collaborators. Runs only for a capture, whose views are built in
RunMode::Capture. See Fixture.
Sourcefn scenario(name: Option<&str>) -> Vec<Invoke<Self::Resource>>
fn scenario(name: Option<&str>) -> Vec<Invoke<Self::Resource>>
The actions that stage the resource for a capture: Some(name) for
--shots --scenario NAME, None for a plain --shots. They apply
through the same door as every control; then the views stage their
view state (ViewCx::on_stage, ResourceSurface::stage) and the
frame is captured. See stage.
Sourcefn scenario_input(
name: Option<&str>,
input: &mut FrameHarness,
) -> Result<(), String>
fn scenario_input( name: Option<&str>, input: &mut FrameHarness, ) -> Result<(), String>
What a person does in the scene that the scenario’s actions and
staged view state cannot show: text typed into an editor, a drag
held over its drop target, a menu opened with a right click. Driven
on input through the window’s own event path after the actions
and staging, so the frame shows the app’s real answer to that input.
Must be deterministic, like the rest of the scenario.
Sourcefn seed(cx: &SeedCx<'_>) -> Vec<Invoke<Self::Resource>>
fn seed(cx: &SeedCx<'_>) -> Vec<Invoke<Self::Resource>>
What a brand-new record starts with that depends on who creates it or
where: the creator as its owner, a first-run import of settings that
already exist on this device. Everything that depends on neither is
the resource’s initialize, which is the same
on every device so that two people creating the same record converge
on one.
The actions apply once, right after the genesis, attributed to the creator and recorded as content from outside: they sync like any edit but are nobody’s undo step, so a first Ctrl+Z does not take the seed back. A collaborator who creates the same record concurrently seeds it too, so a seed sets values rather than adding rows.
Sourcefn records(records: &mut Records<'_>)
fn records(records: &mut Records<'_>)
The records the app keeps besides the one it shows: the person’s own
records and the settings sections that are resource models. Each is
opened with the window and published beside the app’s resource, so
the palette and agents change it whether or not a view has shown it,
and tilo-app surface commits its surface with the app’s.
fn records(records: &mut Records<'_>) {
records.person::<WatchHistory>().settings::<Playback>();
}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.