Style Context Tutorial
Last updated
Last updated
Use the style context to create or modify Bike outline styles.
.
Entry point style/main.ts
Use to define custom stylesheets for Bike’s outline editor.
Import bike/style context API using import { SYMBOL } from 'bike/style'
.
Note: Finished tutorial can be found in .
Each outline style is ordered list of style rules.
Each rule is composed of an and a callback function.
The following steps are followed to compute a style:
Starts with a default style object
Constructs an ordered list of rules matching the element
Passes the style object into the callback function of each matching rule
Through this process the default style object is transformed into a specific style
The order that you define your rules is important. Generally you want generic style rules listed first and refinements listed later.
The rule callbacks must be pure functions. Given the same editor state and style input values they must always generate the same end style state. They should only read values from the editor and style parameters when deciding what style values to set.
Styles are not inherited from parents as they are in CSS. Instead you should create a generic rule that matches all elements and sets defaults there. Put this generic rule at the start of your theme and then follow it with more specific rules.
Let's create a new completely empty outline style. Then we'll add in the essential rules that will make your outline look like an outline.
First, define a new outline style in style/main.ts
:
Build and install your extension
Select the style in Bike > Window > Style Sheets > Tutorial
Your outline has no indentation or formatting. Those things are all defined by outline styles, and your style has no rules.
Outline editing behavior is still there. You can still expand/collapse lines. You can still insert text. You can still select and edit text, though it will be difficult because you won't see selection marks.
Modify style/main.ts
to add back visual structure:
When adding rules we always add them to a layer. This helps to organize them, and makes it possible to modify existing styles by adding new rules to a specific layer.
Once you have rebuilt and installed your extension you should see outline structure.
This rule that you've added matches all rows and adds padding. Parent rows contain their child rows, so the extra padding added to the left edge indents those children.
To see the structure more clearly replace that rule with this rule:
The visual structure of your outline is now be apparent. Rows (blue border) contain text (green border) and potentially other child rows.
The blue and green rectangles are created by attaching decorations to the row and to the row's text. Decorations can also be attached to runs of the row's text. Decorations are attached to the underlying text layout, but they don't effect it. If you need to make space for a decoration add padding to the element you are decorating.
Let's make it so that you can see what text is selected. To do that we'll create a new kind of rule that matches runs of text. We'll also add this rule to the "selection" layer for organization.
Try adding this rule, and then select some text in Bike:
You should see text selections now when you select within a single paragraph. They will disappear when you select multiple paragraphs, but we'll fix that eventually.
A potential question, how would you even know about that @view-selected-range
attribute? This is where the outline path explorer is useful. Select Window > Outline Path Explorer. Then make sure that "Show View Attributes" is selected. Now make some selections.
You should see the @view-selected-range
attribute show up in the outline path explorer when you select a range of text. You can also type .@view-selected-range
into the outline path explorers search field, and then the selected range of text will be highlighted green.
You can use the outline path explorer to find attributes that you can use when styling, and you can also see which elements will be selected by your outline paths when the editor is in various states.
In our current selection rule we are setting the runs background color directly. This is setting core text's background color attribute. This works, but it's more flexible to use decorations.
Replace the selection layer with a decoration based approach like this:
This is similar to how we used decorations to draw boxes around rows and row's text. One difference is that in this case we had to set the zPosition
property. We want to make sure that the text selection draws behind the run's text. An alternative design would be to draw in front of the row's text, but make the selection color partially transparent.
In Bike there are two selection modes–text selection mode and block selection mode. So far we are only showing text selections. When you extend the selection beyond a single paragraph Bike starts using block selection mode.
Lets show block selections by replacing the selection layer with:
A few interesting things are happening here.
The matching outline path is calling the selection
function with a block
parameter value. This function will return true if the current node has block selection.
For this example I am reusing the "background" decoration and changing its style. For example if I give the background rounded corners in my first rule, then the block selection will also have rounded corners. Alternativly I could have created a new decoration with a new ID to indicate block selection.
Inline formatting, such as bold and italic, is applied at the text run level.
Add support for bold and italic text:
While these rules are simple you can add decorations to text runs just like you can add them to rows and row's text. Try creating a rule to show text with the @highlight
attribute. You can add/remove that attribute from text by using Format > Highlight.
So far we've used decorations as simple backgrounds, but they can do more. They can be placed and sized. They can contain images, symbols, and text. Let's use decorations to style a task item with a checkbox. Start by adding this rule:
With that rule in place when you create a task in your outline it should show up with a red background.
Decorations are more then just colors, they can also show images using the contents property. Update the row formatting layer to show a checkbox image:
We are creating a SF Symbol configuration. Then we use that configuration to create an image. Finally we assign that image to the decorations contents. When you save this new rule you should see an empty checkbox centered over all task rows.
Next we need to position the checkbox in a more reasonable location:
Decorations have x
, y
, width
, and height
properties of type LayoutValue
. You get layout values from the passed in layout
parameter. These are logical values that are resolved later in the layout process to position the decoration.
The checkbox is looking good!
Next we need to make it so that the box is checked when the tasks is done. When a task is marked done that is indicated by adding a @done
attribute to the task's row.
Here's the final styling, supporting both checked and unchecked tasks:
You can test the task style by selecting a task, press the escape key to enter block selection mode, and then type m d
to toggle the done status of the task. This is made possible by a keybinding defined in the !bike.bkext extension.
The task checkbox should toggle!
One more thing.
Add that line to the @done state rule and done tasks will get strikethrough text.
Each rule callback function takes two parameters–editor and style object. So far we've just been modifying the style object, but we can also read from the editor object to get editor settings, user theme settings, and more.
Your outline style should try to include the editor settings where it makes sense.
For example you might add these lines to the first match all .*
rule:
Now when you View > Text Size > Zoom In/Out the outline text size will change in your theme. In addition to user settings the editor also includes system state such as isKey
(does the view have keyboard focus) or isTyping
(true when user is typing, false once they move mouse).
I hope the tutorial has helped you understand how styles work. Creating a full style that supports all of Bike's features is a lot of work. You can see Bike's standard style in src/!bike.bkext/style/bike-style.ts
.
Here are some ways to approach outline styles:
It may be that you don't need to create a whole new style, maybe you just want to add a few rules to existing style(s). You can do this using the defineOutlineStyleModifier
API. This allows you to insert rules into existing outline styles.
It maybe be that you do want to create a whole new style, but you want to import rules into that style from other outline styles. For example maybe you want to include the "run-formatting" rules from the standard style.
You can include rules from other styles like this:
Last, you can copy an existing style. Rename it, and make modifications.