Application Resources
The aim of this tutorial is to explain the arrangement of application resources and the data format employed. Let's discover what kinds of resources are supported by the RUI library, as well as how they can be incorporated into our application.
Resource types
RUI library support the following types of resources:
- Images (
jpeg
,png
,gif
,svg
,webp
andbmp
) - Audio (
flac
,mp3
,mp4
,ogg
,webm
) - Video (
mp4
,ogg
,webm
) - UI description (
rui
) - Themes (
rui
) - Translations (
rui
) - Raw data (any format)
Resources can either be embedded in the application's binary file or located on a local storage.
Below is the typical layout of the resource files which are embedded into the application:
All resources for the application are located in the "resources" folder. Each subfolder serves a specific purpose to organize files based on their meaning. Here's a brief overview of each folder:
Folder | Description |
---|---|
views | This folder contains files in "rui" data format that describe the user interface of the application |
strings | This folder contains text translation files for different languages in "rui" data format |
themes | This folder contains files in "rui" data format that describe user interface themes |
images | This folder contains image files used by the application's user interface |
raw | This folder contains other types of files, such as audio or video, that are used by the application |
Embedding resources
To incorporate resources files into the application's binary, we utilize the embed Go package.
Example
Go
package main
import "embed"
//go:embed resources
var resources embed.FS
This code will embed whole "resource" folder content into the application's binary and create a convenient variable to reference that content. The library won't know about this variable, so we need to explicitly direct it to use these resources as our application's resources:
Go
rui.AddEmbedResources(&resources)
This call is usually done before executing a RUI library's main loop (rui.StartApp()
).
If application generates and use some dynamic content(uploaded or generated data) it may utilize a local storage. To be able to reference that local storage in RUI library we've to specify its path:
Go
rui.SetResourcePath(path string)
where "path" could be an absolute or relative path to an existing folder. Usually that folder has the same layout as "resources" folder has.
When we call rui.StartApp()
, the library will automatically look for application resources, whether we have set them or not.
It does this by checking for a "resources" folder in current directory.
If folder has been found, the library will attempt to load all supported resources from it.
Make sure that this folder has the correct structure and layout to ensure everything works properly.
The library processes resources in a specific order:
- Embedded resources
- Resources from the specified path
- Resources from the current directory
For example, if we've embedded translations or themes in our application, we can update or add more of such data by placing new files in the "resources" folder of our application's current directory.
Alternatively, we can specify an additional resources folder using the rui.SetResourcePath()
function to store these updated or extra resources.
This approach allows us to customize and enhance our application without needing to rebuild it from scratch.
Resource file format
Now let's find out how the "rui" resource file data format looks like.
This is a text-based file in UTF-8 encoding that is stored with a ".rui" extension. The file format is quite similar to JSON, but there are some minor differences.
The root element of the file must be an object, and it has the following structure:
<object name> {
<object data>
}
Examples
RUI
TextView {
text = Hello,
}
RUI
strings {
en = _{
hello = Hello,
}
}
RUI
theme {
constants = _{
defaultPadding = 4px,
}
}
Object name must be in quotes if it contains any of these symbols: '=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/' and any type of spaces.
We could use any of these three types of quotes:
-
"…"
- same meaning as Go language strings, we could use escape sequences like \n, \r, \, \", \', \0, \t, \x00, \u0000 -
'…'
- same as above -
`…`
- same meaning as Go language strings, text interpreted as is including new lines and tabs. We can't use`
inside.
Object data is a sequence of a <key> = <value>
pairs separated by comma.
Key is a string and follow the same rules as an object name, but values could be of three types.
Simple value - a string which follow the rules of an object name.
Examples
RUI
text = Hello,
RUI
text = "Hello world",
An object - same as the root element of resource file.
Examples
RUI
content = EditView {
text = Hello,
},
RUI
background = radial-gradient {
gradient = "#FF008000,#FF000000",
},
RUI
border = _{
style = solid,
width = 1px,
},
An array - a list of objects or values separated by comma.
Examples
RUI
content = [
EditView {
text = "Line 1",
},
EditView {
text = "Line 2",
},
],
RUI
data-list = [
#FFFF1010,
#FF10FF10,
#FF1010FF,
],
RUI
shadow = [
_{
blur = 2px,
color = black,
spread-radius = 2px,
},
_{
inset = true,
blur = 2px,
color = black,
spread-radius = 2px,
},
]
RUI
theme {
styles = [
demoPage {
width = 100%,
height = 100%,
cell-width = "1fr, auto",
},
demoPanel {
width = 100%,
height = 100%,
orientation = horizontal,
},
]
}
File can contain comments which follow the Go language style (//
, /* ... */
).
RUI
/*
* Root view of the app
*/
StackLayout {
width = 100%,
height = 100%,
// An array of pages
content = [
// Layout of the first page
ListLayout {
content = EditView {
text = "Page 1",
},
},
// Layout of the second page
ListLayout {
content = EditView {
text = "Page 2",
},
},
],
}
Below is an example of description of UI controls in "rui" resource file.
RUI
// Root view
GridLayout {
id = gridLayout, width = 100%, height = 100%,
cell-width = "150px, 1fr, 30%", cell-height = "25%, 200px, 1fr",
content = [
// Sub views
TextView { row = 0, column = 0:1,
text = "View 1", text-align = center, vertical-align = center,
background-color = #DDFF0000, radius = 8px, padding = 32px,
border = _{ style = solid, width = 1px, color = #FFA0A0A0 }
},
TextView { row = 0:1, column = 2,
text = "View 2", text-align = center, vertical-align = center,
background-color = #DD00FF00, radius = 8px, padding = 32px,
border = _{ style = solid, width = 1px, color = #FFA0A0A0 }
},
TextView { row = 1:2, column = 0,
text = "View 3", text-align = center, vertical-align = center,
background-color = #DD0000FF, radius = 8px, padding = 32px,
border = _{ style = solid, width = 1px, color = #FFA0A0A0 }
},
TextView { row = 1, column = 1,
text = "View 4", text-align = center, vertical-align = center,
background-color = #DDFF00FF, radius = 8px, padding = 32px,
border = _{ style = solid, width = 1px, color = #FFA0A0A0 }
},
TextView { row = 2, column = 1:2,
text = "View 5", text-align = center, vertical-align = center,
background-color = #DD00FFFF, radius = 8px, padding = 32px,
border = _{ style = solid, width = 1px, color = #FFA0A0A0 }
},
]
}
Check out the library Reference documentation on how to create a particular UI control from resource file and which properties of which types it may contain including the events.
Theme files
Theme files are located in "themes" folder and use "rui" file format for description.
The root object within a theme file is called theme
.
Example
RUI
theme {
name = "My theme",
}
It can contain the following data (properties):
Property | Description |
---|---|
name | Name of the theme. If not set or contains an empty string then this theme is a default one |
constants | An object which contains a key-value pairs of constants. Object may have any name, we recommend to use _ because it is not relevant |
constants:touch | Same as constants but for clients with touch screen support |
colors | An object which contains a key-value pairs of color constants for light theme (default one) |
colors:dark | Same as colors but for dark theme mode |
images | An object which contains a key-value pairs of image constants for light theme (default one) |
images:dark | Same as images but for dark theme mode |
styles | An array of objects which describe common styles |
styles:portrait | Same as styles but for clients with portrait window layout |
styles:landscape | Same as styles but for clients with landscape window layout |
styles:width\<min>-\<max> | Same as styles but for client window width between min and max values in logical pixels |
styles:width\<max> | Same as styles but for client window width not exceeds that max value in logical pixels |
styles:width\<min>- | Same as styles but for client window width greater than min value in logical pixels |
styles:height\<min>-\<max> | Same as styles but for client window height between min and max values in logical pixels |
styles:height\<max> | Same as styles but for client window height not exceeds max value in logical pixels |
styles:height\<min>- | Same as styles but for client window height greater than min value in logical pixels |
We can change applications theme at any time using SetCustomTheme()
method of the Session
interface.
Example
Go
session.SetCustomTheme("My theme")
Constants
Object that define constants can include any number of key-value pairs.
The values can be text, numbers, or string representations of built-in types such as rui.SizeUnit
and rui.AngleUnit
.
Example
RUI
theme {
name = "My theme",
constants = _{
defaultPadding = 4px,
angle = 30deg,
}
}
If we want to reference one constant within another constant, we must use the @
symbol before the constant name.
RUI
theme {
constants = _{
defaultPadding = 4px,
buttonPadding = @defaultPadding,
}
}
The order in which we define constants does not matter.
To retrieve a constant value, we can use the Constant()
method of the Session
interface.
This will return the constant value from the current theme of the client session:
Go
val, ok := session.Constant("defaultPadding")
If constants referencing each other then this method will return an empty string. When reading the value of a constant that references another constant, we will receive the final value.
Additionally, when setting a constant as the value of a UI control property, we must use the '@' symbol before the constant name.
Go
rui.Set(view, "subViewId", rui.Padding, "@defaultPadding")
When defining constants, we can set different values based on whether the client device has a touch screen or not.
Example
RUI
theme {
constants = _{
defaultPadding = 4px,
},
constants:touch = _{
defaultPadding = 12px,
},
}
In the previous example, if the client device supports touch screen and we read the constant value from the session, we will receive "12px" as the final value.
Color constants
The values of color constants are string representations of rui.Color
type.
Example
RUI
theme {
colors = _{
textColor = #FF101010,
backgroundColor = white,
}
}
Color constants adhere to the same rules as regular constants and can also reference other color constants.
Built-in color values such as black
, white
, and others do not require the @
prefix.
Example
RUI
theme {
colors = _{
red = blue,
}
}
For the given example, the following Go code makes sense:
Go
rui.Set(view, "subViewId", rui.TextColor, "@red") // assign blue color
rui.Set(view, "subViewId", rui.TextColor, "red") // assign red color
To retrieve the current color constant value, we can use the Color()
method of the Session
interface:
Go
val, ok := session.Color("textColor")
When defining color constants, we can set different values based on whether the client device is in light or dark theme mode.
Example
RUI
theme {
colors = _{
ruiTextColor = black,
},
colors:dark = _{
ruiTextColor = white,
},
}
In the previous example, if the client device operates in dark theme mode and we read the ruiTextColor
constant from the session, we will receive white
as the final value.
Image constants
The values of image constants are names of image resource files.
Example
RUI
theme {
images = _{
backgroundImage = bg-image.jpg,
},
images:dark = _{
backgroundImage = bg-image-dark.jpg,
},
}
Image constants adhere to the same rules as color constants and can also reference other image constants.
To retrieve the image constant value, we can use the Image()
method of the Session
interface:
Go
img, imgDark := session.Image("backgroundImage")
Image constants can have different values based on whether the client device is in light or dark theme mode.
Styles
Styles enable us to group multiple UI control style properties together.
Example
RUI
theme {
styles = [
demoPage {
width = 100%,
height = 100%,
cell-width = "1fr, auto",
},
demoPanel {
width = 100%,
height = 100%,
orientation = start-to-end,
},
]
}
In the given example, "demoPage" and "demoPanel" are the names of the styles.
UI controls typically provide a way to apply custom styles described in theme files.
For instance, a basic View
has "style" and "style-disabled" properties for this purpose.
Here is how we can set them:
Go
rui.Set(view, "subViewId", rui.Style, "demoPage")
rui.Set(view, "subViewId", rui.StyleDisabled, "demoPageDisabled")
Styles can be automatically applied based on specific client device settings.
Example
RUI
theme {
styles = [
demoPage {
width = 100%,
height = 100%,
cell-width = "1fr, auto",
},
demoPage2 {
row = 0,
column = 1,
}
],
styles:landscape = [
demoPage {
width = 100%,
height = 100%,
cell-height = "1fr, auto",
},
demoPage2 {
row = 1,
column = 0,
}
],
}
In the given example, "demoPage" and "demoPage2" styles will have different settings based on the client device's window dimensions (landscape or portrait).
Additionally, style may have several constraints listed on one line.
Example
RUI
theme {
styles:portrait:width320 = [
samplePage {
width = 100%,
height = 100%,
},
],
styles:portrait:width320-640 = [
samplePage {
width = 100%,
height = 70%,
},
],
styles:portrait:width640- = [
samplePage {
width = 100%,
height = 50%,
},
],
styles:portrait:width640-:height480- = [
otherPage {
width = 100%,
height = 100%,
},
],
}
In the given example, if the client device's window is in portrait mode, styles will be automatically applied based on the client's window width or height in logical pixels.
It is possible to override built-in constants and styles in our theme files.
Color constants that can be overridden:
Color constant name | Description |
---|---|
ruiBackgroundColor | Background color |
ruiTextColor | Text color |
ruiDisabledTextColor | Disabled text color |
ruiHighlightColor | Highlight color |
ruiHighlightTextColor | Highlighted text color |
ruiButtonColor | Button color |
ruiButtonActiveColor | Focused button color |
ruiButtonTextColor | Button text color |
ruiButtonDisabledColor | Disabled button color |
ruiButtonDisabledTextColor | Disabled button text color |
ruiSelectedColor | Background color of inactive selected list view item |
ruiSelectedTextColor | Text color of inactive selected list view item |
ruiPopupBackgroundColor | Popup background color |
ruiPopupTextColor | Popup text color |
ruiPopupTitleColor | Popup title background color |
ruiPopupTitleTextColor | Popup title text Color |
ruiTooltipBackground | Tooltip background color |
ruiTooltipTextColor | Tooltip text color |
ruiTooltipShadowColor | Tooltip shadow color |
Regular constants that can be overridden:
Constant name | Description |
---|---|
ruiButtonHorizontalPadding | Horizontal padding inside the button |
ruiButtonVerticalPadding | Vertical padding inside the button |
ruiButtonMargin | External button space |
ruiButtonRadius | Button corner radius |
ruiButtonHighlightDilation | Width of the outer border (highlight) of the active button |
ruiButtonHighlightBlur | Blur the active button outer border (highlight) |
ruiCheckboxGap | Break between checkbox and it's content |
ruiListItemHorizontalPadding | Horizontal padding inside a list view item |
ruiListItemVerticalPadding | Vertical padding inside a list view item |
ruiPopupTitleHeight | Popup title height |
ruiPopupTitlePadding | Popup title padding |
ruiPopupButtonGap | Break between popup buttons |
Styles which can be overridden:
Style name | Description |
---|---|
ruiApp | Used to set the default text style (font, size, etc.) |
ruiView | Default View style |
ruiArticle | Used if the "semantics" property is set to "article" |
ruiSection | Used if the "semantics" property is set to "section" |
ruiAside | Used if the "semantics" property is set to "aside" |
ruiHeader | Used if the "semantics" property is set to "header" |
ruiMain | Used if the "semantics" property is set to "main" |
ruiFooter | Used if the "semantics" property is set to "footer" |
ruiNavigation | Used if the "semantics" property is set to "navigation" |
ruiFigure | Used if the "semantics" property is set to "figure" |
ruiFigureCaption | Used if the "semantics" property is set to "figure-caption" |
ruiButton | Used if the "semantics" property is set to "button" |
ruiParagraph | Used if the "semantics" property is set to "paragraph" |
ruiH1 | Used if the "semantics" property is set to "h1" |
ruiH2 | Used if the "semantics" property is set to "h2" |
ruiH3 | Used if the "semantics" property is set to "h3" |
ruiH4 | Used if the "semantics" property is set to "h4" |
ruiH5 | Used if the "semantics" property is set to "h5" |
ruiH6 | Used if the "semantics" property is set to "h6" |
ruiBlockquote | Used if the "semantics" property is set to "blockquote" |
ruiCode | Used if the "semantics" property is set to "code" |
ruiTable | Default table view style |
ruiTableHead | Default table view header style |
ruiTableFoot | Default table view footer style |
ruiTableRow | Default table view row style |
ruiTableColumn | Default table view column style |
ruiTableCell | Default table view cell style |
ruiDisabledButton | Default button style if property "disabled" is set to true |
ruiCheckbox | Default checkbox style |
ruiListItem | Default list view item style |
ruiListItemSelected | Default list view item style when list view has no focus |
ruiListItemFocused | Default style for selected list view item when list view has focus |
ruiPopup | Default popup style |
ruiPopupTitle | Default popup title style |
ruiMessageText | Default popup text style (Message, Question) |
ruiPopupMenuItem | Default popup menu item style |
For further information on defining an application's theme, we can refer to the "defaultTheme.rui" file located within the RUI library's source code.
Localization files
Localization files are also described in 'rui' file format and stored within the 'strings' subdirectory of the application's resources.
The primary object name for this file is 'strings'.
strings {
<language 1> = _{
<text constant 1> = <text translation 1>,
<text constant 2> = <text translation 2>,
…
},
<language 2> = _{
<text constant 1> = <text translation 1>,
<text constant 2> = <text translation 2>,
…
},
…
}
where "\<language N>" is the name of the language, examples: "en", "en-GB", "de" and so on. "\<text constant N>" is a string representation of the text constant, example: "tr-hello", "ok", "app title" and so on. "\<text translation N>" is a translated string to a specific language.
These language names are retrieved from the client's settings, such as the browser.
It is possible to define translations in separate files, and it would be convenient to name these files using the language name they correspond to.
Here is a description of the format for such files:
strings:<language> {
<text constant 1> = <text translation 1>,
<text constant 2> = <text translation 2>,
…
}
where "\<language>" is the name of the language.
Examples
Description of translations in one file "strings.rui":
RUI
strings {
en = _{
"Yes" = "Yes",
"No" = "No",
},
de = _{
"Yes" = "Ja",
"No" = "Nein",
},
}
Description of translations for English and German languages in different files.
File "en.rui":
RUI
strings:en {
"Yes" = "Yes",
"No" = "No",
}
File "de.rui":
RUI
strings:de {
"Yes" = "Ja",
"No" = "Nein",
}
To be able to use translated text in UI controls we've to refer to text constant:
RUI
TextView {
text = "Yes",
}
Translations are automatically applied by the library in all UI controls, with the exception of drawing the text in CanvasView
.
To obtain translations, we must use the GetString()
method provided by the Session
interface, for example:
Go
val, ok := session.GetString("Decline")
If a specific translation for a given string is unavailable, the GetString()
method will return the name of the constant.
In this example, it would be "Decline".
To determine the current language used by the client session, we can utilize the Language()
method provided by the Session
interface.
Go
lang := session.Language()
If application allows the user to set preferred user interface language then SetLanguage()
method of the Session
interface is what we need.
Images for screens with different pixel density
If we need to provide separate images for screens with varying pixel densities, this can be achieved using an iOS-style naming convention.
Simply append @<density>x
to the end of filename of each image resource:
image@1.5x.png
image@2x.png
image@3x.png
For the listed image resources, we would only need to assign the value "image.png" to the "src" property of our ImageView
UI control:
RUI
ImageView {
width = 15em,
height = 15em,
fit = cover,
src = "image.png",
}
The library will then locate and transfer the appropriate image from the "images" sub-directory based on the client's screen pixel density.