Input¶
A text box the reader types into. What they type is held for as long as the book is open and read back by conditions, which is how you build a page that filters itself as the reader searches.
{
"Type": "Input",
"InputId": "search",
"TexturePath": "Assets/PeacefulEnd.Parchment/panelFrame1",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 18, "Height": 18 },
"Placeholder": "Search...",
"FontType": "Small",
"Scale": 2,
"Padding": 3,
"MaxLength": 32
}
InputId is required. It's the handle everything else uses to reach the text, so give it something you'll recognise in a condition.
Input fields¶
| Property | Type | Default | Description |
|---|---|---|---|
InputId required |
string |
— | The name conditions and actions use to reach this input's text. Expected to be unique within the book. |
Placeholder optional |
string |
— | A prompt shown while the box is empty. Conditions see an empty input, not this. |
PlaceholderColor optional |
string |
a faded TextColor |
The prompt's color, as a name such as "Gray" or a value such as "128 128 128". |
MaxLength optional |
int? |
— | The most characters the reader can type. Unbounded when omitted. |
Padding optional |
int |
0 |
Space between the frame's inner edge and the text, in unscaled sprite pixels × Scale. |
Sizing optional |
sizing mode |
Fill |
How wide the box is. ShrinkToFit hugs the Placeholder rather than the typed text, so the box doesn't grow and shrink under the reader as they type. |
Width optional |
int? |
— | The content width in unscaled sprite pixels × Scale. Required when Sizing is Fixed. |
Height optional |
int? |
one line of text | The content height in unscaled sprite pixels × Scale. Set it when you want Scale to size the box's height the way it sizes its width. |
TextScale optional |
number |
1 |
The text's scale, independent of Scale, which sizes the frame. |
SubmitAction optional |
string |
— | A trigger action run when the reader presses enter. Understands tokens. |
SubmitActions optional |
list of string |
— | Trigger actions run in order on enter. Combined with SubmitAction rather than replacing it. Understands tokens. |
TextChangedAction optional |
string |
— | A trigger action run once the text has stopped changing. Understands tokens. See Reacting to typing. |
TextChangedActions optional |
list of string |
— | Trigger actions run in order once the text settles. Combined with TextChangedAction rather than replacing it. Understands tokens. |
TextChangedDelay optional |
number |
250 |
How long the text has to sit still before the text changed actions run, in milliseconds. Each change restarts the wait. |
Text is the box's starting text rather than its label. The reader can edit it, and clearing the box doesn't bring it back.
Filtering a list¶
The point of an input is that other elements can condition themselves on it. PeacefulEnd.Parchment_InputMatches is true when the typed text appears in the text you give it, and true for everything while the box is empty:
{
"Elements": [
{ "Type": "Input", "InputId": "search", "TexturePath": "{{ModId}}/box", "Placeholder": "Search..." },
{ "Type": "Paragraph", "Text": "Tulip", "Condition": "PeacefulEnd.Parchment_InputMatches search Tulip" },
{ "Type": "Paragraph", "Text": "Blue Jazz", "Condition": "PeacefulEnd.Parchment_InputMatches search Blue Jazz" },
{ "Type": "Paragraph", "Text": "Nothing found.", "Condition": "PeacefulEnd.Parchment_HasInputText search" }
]
}
Typing narrows the list on the next keystroke, and the elements below close the gap because a hidden element takes up no space. Matching ignores case, and everything after the input's ID counts as the text being tested, so a phrase needs no quoting.
The list has to fit the page
Parchment doesn't reflow. Filtering hides elements, it doesn't move them onto another page, so a search over more entries than a page holds shows the first screenful and drops the rest with a warning. Keep the candidate list to a page, or narrow it with a second condition.
Passing the text to an action¶
%Input% in any action is replaced with the box's current text just before the action runs. On the input itself the bare form means its own text, elsewhere name the box:
{
"Type": "Input",
"InputId": "entry",
"TexturePath": "{{ModId}}/box",
"Placeholder": "Go to entry...",
"SubmitAction": "PeacefulEnd.Parchment_JumpToPageId %Input%"
}
Paired with JumpToPageId that gives you a box the reader types a page's Id into. It's an exact match rather than a search, so it suits a book whose pages have names worth typing.
{
"Type": "Button",
"TexturePath": "{{ModId}}/button",
"Text": "Go",
"Action": "PeacefulEnd.Parchment_JumpToChapter %Input:chapterBox%"
}
The text is substituted already quoted, so a typed phrase stays a single argument. See Tokens.
Reacting to typing¶
Conditions already keep up with the reader on their own, so a list filtered with Parchment_InputMatches needs nothing here. TextChangedAction is for the work that shouldn't happen on every keystroke: setting a flag, jumping to a results page, anything with a cost.
{
"Type": "Input",
"InputId": "search",
"TexturePath": "{{ModId}}/box",
"Placeholder": "Search...",
"TextChangedAction": "PeacefulEnd.Parchment_SetFlag searching",
"TextChangedDelay": 250
}
The wait restarts on every change, so typing a word runs the actions once, when the reader pauses. TextChangedDelay: 0 runs them on the next tick after each keystroke instead.
The text is watched rather than hooked to the keyboard, so anything that changes it counts, including Parchment_SetInput and a clear button. That's what lets a clear button reset whatever the search put in place, without the book having to run the same actions from two places.
The starting text isn't a change
An input's authored Text is recorded the first time the element is looked at, without running anything, so a book doesn't fire its text changed actions the moment it opens.
Gotchas¶
Only one box has the keyboard. Clicking a box focuses it, clicking anywhere else drops focus. While a box is focused every other key is taken over, so the chat hotkey, the book's own keybinds and other mods' hotkeys all stop firing until focus is dropped. Escape is the exception, leaving the box on the first press and closing the book on the second.
Focus follows the box, not the page. A box on a page loses focus when that page is turned away from, since it's no longer on screen. One on the book's own Underlay or Overlay keeps it through a turn, which is what lets a search box stay live while the reader jumps between results. Shutting to the cover or closing the book drops focus either way.
The text is per reading session. Closing the book empties every input. Nothing is saved, so a search box starts blank each time the book is opened.
Controllers get the on-screen keyboard. Pressing A on a box with snappy menus on opens the game's own keyboard rather than waiting on a hardware one, and closing it submits. Clicking the box with a mouse still takes the hardware keyboard. See controller support.
Sprite fields¶
The texture must nine-slice: the border is a third of the shorter side. HoverTextureSourceRectangle gives the box a highlighted state under the cursor. The frame is optional, so a box with no TexturePath draws its text with nothing behind it, which suits an input sitting inside a Panel.
| Property | Type | Default | Description |
|---|---|---|---|
TexturePath optional |
string |
— | The asset name of the sprite sheet, such as Assets/PeacefulEnd.Parchment/panelFrame2 or a vanilla path like LooseSprites/Cursors. This is a game asset name, not a file path in your content pack. Load your PNG into the game's content with a Content Patcher Load patch first. See Loading your art. |
TextureSourceRectangle optional |
Rectangle |
the whole texture | The area of the sprite sheet to draw. Almost always needed: without it, the whole sheet is used, which for a multi-sprite sheet means a much larger element than you expect. |
HoverTextureSourceRectangle optional |
Rectangle? |
— | An alternate source rectangle drawn while the cursor is over the element. Must be the same size as TextureSourceRectangle. The element's layout is measured from the normal rectangle, so a differently-sized hover sprite will overhang its own bounds. For a moving hover state on an Image, use HoverFrames instead. |
TintColor optional |
color |
white | A color multiplied into the sprite. Multiplying means it can only darken: red on grey art gives red, red on blue art gives near-black. Best on neutral or greyscale art. |
SpriteEffects optional |
None | FlipHorizontally | FlipVertically |
None |
Mirrors the sprite within its own bounds. It does not move the element. If flipping appears to shift the art sideways, the sprite has uneven transparent padding inside its source rectangle. Combine with a comma: "FlipHorizontally, FlipVertically". Ignored by Panel and Button, which are nine-sliced. |
Text fields¶
Text is always drawn from the left and vertically centred, whatever Alignment says, since Alignment places the box itself. When the text outruns the box its start scrolls out of view, keeping the caret visible.
| Property | Type | Default | Description |
|---|---|---|---|
Text optional |
string |
— | The text to draw. Wraps automatically to the available width, including breaking words that are too long for a line on their own. \n forces a line break. Can carry tokens, both Parchment's %Token% forms and the game's tokenizable strings. |
FontType optional |
font type |
varies | Which font to draw with. The default differs per element type. |
TextColor optional |
color |
the book's default | The text color. |
ShadowColor optional |
color |
the game's shadow color | The color of the drop shadow drawn behind the text, alpha included. Left off, the shadow follows TextColor's alpha instead. Ignored when FontType is SpriteText, which draws its own outline. |
Common fields¶
Scale on an Input is the frame scale, sizing the border and the padding. TextScale sizes the text.
That leaves Scale with nothing proportional to grab on the vertical axis unless you give the box a Height. Without one, Width (under Fixed sizing) is multiplied by Scale while the height is just a line of text plus the frame, so raising Scale widens the box far faster than it deepens it. Height fixes that, and setting TextScale alongside Scale keeps the text in proportion with the box.
| Property | Type | Default | Description |
|---|---|---|---|
Type required |
element type |
— | Which kind of element this is. Determines every other field below. |
Id optional |
string |
— | An optional identifier for this element. Not used for navigation, purely for your own reference. |
Alignment optional |
Left | Center | Right |
Left |
Where the element sits within its container's width. Only has an effect when the element is narrower than the space available (see Layout). In a placed list it anchors the element and Position is measured from there. |
VerticalAlignment optional |
Top | Center | Bottom |
Top |
Where the element sits within its container's height. Only used in Background, Foreground, Underlay and Overlay, since a stacked element takes its vertical position from the elements above it (see Placed elements). Setting it on a stacked element logs an error and does nothing. |
Scale optional |
number |
1 |
The element's scale. Its meaning depends on the element type: sprite scale for Image, Panel, Banner, Button and Divider. Font scale for Title, Heading and Paragraph. See Units and scale. |
SpacingAfter optional |
int |
8 |
The gap between this element and the next one, in unscaled sprite pixels × Scale. Not applied after the last visible element, so a trailing gap can't appear at the bottom of a page or panel. |
MarginLeft optional |
int |
0 |
Space reserved to the element's left, in unscaled sprite pixels × Scale. This narrows the width the element measures against, so text wraps at the indented width rather than overflowing. |
MarginRight optional |
int |
0 |
Space reserved to the element's right, in unscaled sprite pixels × Scale. |
Position optional |
Point |
{ X: 0, Y: 0 } |
The element's position, in screen pixels. Only used in Background, Foreground, Underlay and Overlay, where elements are placed rather than stacked. Measured from wherever Alignment and VerticalAlignment anchor the element, so under the default Left and Top it's a plain coordinate (see Placed elements). Unlike every other spacing field this is not multiplied by Scale. Changing an element's scale resizes it in place rather than moving it. |
Condition optional |
string |
— | A game state query. When it evaluates false the element is hidden, and elements below it close the gap. Re-checked several times a second while the book is open. Understands tokens. |
Lifetime optional |
number |
— | Seconds the element stays up once shown. Setting it makes the element a timed one: hidden until a ShowElement action names it, so an Id is required. |
FadeAfter optional |
number |
— | Seconds before it starts fading, reaching nothing at the end of Lifetime. Needs Lifetime, and must be below it. |
IgnoreCursor optional |
bool |
false |
Lets the cursor pass straight through the element to whatever sits beneath it, so it can't be hovered or clicked. Its children and its own Background and Foreground are still reachable (see Passing the cursor through). |
Action optional |
string |
— | A trigger action run when the element is clicked. When set, the element becomes interactive: any element type can have one, not just Button. Understands tokens. |
Actions optional |
list of string |
— | Trigger actions run in order when the element is clicked. Combined with Action rather than replacing it, so an element with both runs Action first. Understands tokens. |
Sound optional |
string |
bigSelect |
The cue played when the element is clicked. Only used when Action or Actions is set, and played once however many actions run. Set to null for a silent click. |
HoverAction optional |
string |
— | A trigger action run when the cursor moves onto the element. Runs on entry, not while the cursor rests there. Sound doesn't apply. Understands tokens. |
HoverActions optional |
list of string |
— | Trigger actions run in order when the cursor moves onto the element. Combined with HoverAction rather than replacing it, so an element with both runs HoverAction first. Understands tokens. |
DisplayName optional |
string |
— | The bold title of the element's hover tooltip. Works in every list, including a page's Background and Foreground. Can carry tokens. |
Description optional |
string |
— | The body of the element's hover tooltip. Works in every list, including a page's Background and Foreground, where setting it is also what makes an otherwise decorative element reachable by the cursor. Ignored when IgnoreCursor is set, which logs a warning. Can carry tokens. |
Tags optional |
list of string |
— | Free-form markers other mods can read off whatever the cursor is over, such as Lookup Anything support. Tagging an element also makes it reachable by the cursor, since a tag is only useful on something that can be hovered. Ignored when IgnoreCursor is set. See Tags. |
ParseTokenizableStrings optional |
bool |
true |
Whether the game's [Token] tokenizable strings are resolved in this element's Text, DisplayName, Description, Condition and actions. Set it to false where a square bracket is meant as the character itself. |