Skip to main content

Style Queries

Basic State Queries

Hover

Hover is active on elements that the mouse or other non-touch pointing device is currently over. It works by finding the deepest element in the list of all elements that intersect or contain the point and then all elements that are ancestors of that element are also marked as hovered.

style hover-me {
[hover] {
BackgroundColor = hotpink;
}
}

Focus

Focus is set explicitly by the runtime when an element becomes focused.

style focus-me {
[focus] {
TextFontSize = 100px;
}
}

Active

Active is triggered on the element that the mouse was down on (following the same hierarchy rules as [hover]) and continues to apply until the mouse is up.

style handle-active {
[active] {
CornerRadiusTopLeft = 50%;
}
}

Init

Init is set on elements for only the frame in which they were first created or became enabled. This is useful for tracking animations values or transitons.

On the first frame set opacity to 0 and then transition it the default value (100%) 
style handle-init {

transition spring(default) = Opacity;

[init] {
Opacity = 0%;
}
}

Hierarchy State Queries

States within an element's hierarchy can be queried using a more advanced form of the state selectors. The examples below will use the focus state but the same behavior applies to hover, active and init as well


style fade-when-ancestor-focused {

[focus ancestor] {
Opacity = 50%;
}

}

style turn-blue-when-child-focused {

[focus child] {
BackgroundColor = blue;
}

}

style round-corners-when-parent-focused {

[focus parent] {
CornerRadius = 25%;
}

}

style padd-left-when-descendant-focused {

[focus descendant] {
PaddingLeft = 25px;
}

}

Attribute Queries

Elements can also be styled based on the presence or lack of attributes that were set either in a ui file or from C#. Styling can be done by either checking if the attribute key exists and matching any value, or by specifically matching only certain values.

style handle-selected {
// no value defined so as long as the element has a 'selected'
// attribute the text will be yellow
[attr:selected] {
TextColor = yellow;
}

// this will only match when the element has the attribute 'selected'
// and the value of that attribute is 'selection1'
[attr:selected="selection1"] {
BackgroundColor = blue;
}

}

We can also style based on the lack of an attribute match

style handle-selected {

// if the element doesn't have a 'selected' attribute, make the text red
[unless attr:selected] {
TextColor = red;
}

// this will match if the element either doesn't have the attribute 'selected',
// or it has the 'selected' attribute but the value is NOT equal to 'selection1'
[unless attr:selected="selection1"] {
BackgroundColor = blue;
}

}

Hierarchy Attribute Queries

Similar to how states can be queried across the hierarchy, attributes can also be queried.

style check-ancestor-attribute {

[when ancestor attr:key] {
}

[when ancestor attr:key = "SomeValue"] {
}
}

style check-children-attribute {
[when child attr:key] {}

[when child attr:key = "SomeValue"] {}
}

style check-descendant-attribute {

[when child attr:key] {}

[when child attr:key = "SomeValue"] {}

}

style check-parent-attribute {

[when parent attr:key] {}

[when parent attr:key = "SomeValue"] {}
}

Child Count Queries

You can also style an element based on how many children it has and even pass in a comparison expression. The numeric comparison value must be a positive integer constant.

style red-when-empty {

// no children
[when empty] {
BackgroundColor = red;
}

// same as [when empty] above
[when child-count(0)] {
BackgroundColor = red;
}

}

style child-count {

[when child-count(== 2)] {}

[when child-count(!= 2)] {}

[when child-count(<= 10)] {}

[when child-count(>= 10)] {}

[when child-count(> 5)] {}

[when child-count(< 5)] {}
}

Child Index Queries

It is often helpful to know if an element you are styling is the first child, or the last child or at some other index. Child Index Queries exist for exactly this purpose and are quite powerful!

Only Child

Matches when the element is the ony child within a parent

style blue-text-when-lonely {
[when only-child] {
TextColor = blue;
}
}

First and Last Child

Matches when the element is the first child of its parent or the last child respectively

style first-child-turns-orange-last-child-turns-green {

[when first-child] {
BackgroundColor = orange;
}

[when last-child] {
BackgroundColor = green;
}

}

Nth-Child

Takes a function expression that describes how children will be matched by this query in a 1-based indexing scheme. Works identically to n-th child expression in css

style fancy-matching {

// turn the element outline white if it is the second child
[when nth-child(2)] {
OutlineColor = white;
}

// turn every fourth element pink
[when nth-child(4n)] {
BackgroundColor = pink;
}

// turn the first three element blue
[when nth-child(-n + 3)] {
BackgroundColor = blue;
}

// turns all elements except the first three yellow
[when nth-child(n + 3)] {
BackgroundColor = yellow;
}

// Turns elements 4 [=(3×0)+4], 7 [=(3×1)+4], 10 [=(3×2)+4], 13 [=(3×3)+4], etc black
[when nth-child(3n + 4) {
BackgroundColor = black;
}

}

Even and Odd Children

Matches when the element is at an even or odd child index


style alternate-fonts {

[when nth-child(odd)] {
TextFontAsset = "Font1";
}

[when nth-child(even)] {
TextFontAsset = "Font2";
}

}

Tag Queries

Elements can also be styled based on their tag name with relational filtering.

style tag-based-styling {

[when tag(TagName)]
[when tag(ModuleName::TagName)]

[when root-tag(TagName)]
[when root-tag(ModuleName::TagName)]

[when parent-tag(TagName)]
[when parent-tag(ModuleName::TagName)]

[when ancestor-tag(TagName)]
[when ancestor-tag(ModuleName::TagName)]

[when child-with-tag(TagName)]
[when child-with-tag(ModuleName::TagName)]

[when descendant-with-tag(TagName)]
[when descendant-with-tag(ModuleName::TagName)]

[when first-with-tag(TagName)]
[when first-with-tag(ModuleName::TagName)]

[when last-with-tag(TagName)]
[when last-with-tag(ModuleName::TagName)]

[when only-with-tag(TagName)]
[when only-with-tag(ModuleName::TagName)]

}

Condition Queries

Sometimes you just need more power. Maybe you're implementing a resposive design or maybe you have a game that enters a certain state and you want to change the look of some elements based on game state. With condition queries you can make up your own query logic and have the style system respond to it. Unlike the other query types listed so far, condition queries require some C# code to work.

Here is how to define them. First we need to make callback that matches the delegate type.


delegate bool StyleConditionEvaluator(UIView view, DeviceInfo deviceInfo);

// DeviceInfo has the following interface:

struct DeviceInfo {
float dpiScale;
float GetScaledScreenWidth();
float GetScaledScreenHeight();
}

This example will style an element differently depending on the aspect ratio of our screen. So let's provide conditions callbacks to check if width is larger than height and vice versa; While this example is super simple, you can run any code you want in this function. Maybe you want to check if your player is injured, or possesses some item, or if the internet connection got interrupted in an MMO game... you can make this do whatever you like.

StyleConditionEvaluator widthLarger = (UIView view, DeviceInfo deviceInfo) => {
return Screen.width > Screen.height;
};

StyleConditionEvaluator heightLarger = (UIView view, DeviceInfo deviceInfo) => {
return Screen.height > Screen.width;
};

Now we need to register these functions with our UIApplication using the SetStyleCondition api.

UIApplication application; // assume you set this somehow, probably from your UIRoot.

application.SetStyleCondition("WidthIsLarger", widthLarger);
application.SetStyleCondition("HeightIsLarger", heightLarger);

The evaluator functions are called every frame and are invoked once per active view in the application (Hence the UIView argument in the StyleConditionEvaluator delegate). Now that our functions are setup, let's write a style that can use them.

style size-based-on-screen-aspect {

// if the screen width is larger than height, make our width 500px
[when "WidthIsLarger"] {
PreferredWidth = 500px;
}

// if the screen width is smaller than height, make our width 250px
[when "HeightIsLarger"] {
PreferredWidth = 250px;
}

}