mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-17 19:13:19 +03:00
- Rename .agent to .agents and update plan references - Add an articles index and clearer links to the UI inspector - Correct the UI introduction’s CSS inheritance guidance and add an example workflow - Add F11 visual inspection to the hello-world example and enlarge its window to fit - Remove the old Kitty keyboard plan and TODO.md
309 lines
12 KiB
Markdown
309 lines
12 KiB
Markdown
# UI Introduction
|
|
|
|
## Introduction
|
|
|
|
eepp UI is designed to be flexible and efficient. Inspired in
|
|
[Android Layouts structure](https://developer.android.com/guide/topics/ui/declaring-layout)
|
|
and [CSS standards](https://developer.mozilla.org/en-US/docs/Web/CSS).
|
|
It's still a work in progress so several features are still not stable, but
|
|
already provides a very solid foundation to create rich and interactive UIs.
|
|
|
|
For other guides organized by task, see the [articles index](README.md).
|
|
|
|
## Layout
|
|
|
|
A layout defines the structure for a user interface in your app.
|
|
For constructing layouts in eepp we have two options: instantiate layout
|
|
elements at runtime or declare UI elements in XML.
|
|
|
|
* **Declare UI elements in XML.** eepp provides a straightforward XML
|
|
vocabulary that corresponds to the UI Widget classes and subclasses.
|
|
There are two kinds of widgets: normal widgets and layouts. The difference
|
|
between this two is that layouts are the ones in charge of arranging their
|
|
child in a particular way.
|
|
|
|
* **Instantiate layout elements at runtime.** Your app can create Widget and
|
|
Layout objects (and manipulate their properties) programmatically.
|
|
|
|
Declaring your UI in XML allows you to separate the presentation of your app
|
|
from the code that controls its behavior. Using XML files in conjunction with
|
|
CSS files also makes it easy to provide different layouts for different screen
|
|
sizes.
|
|
|
|
The framework gives you the flexibility to use either or both of these methods
|
|
to build your app's UI. For example, you can declare your app's default
|
|
layouts in XML, and then modify the layout at runtime.
|
|
|
|
## Write the XML
|
|
|
|
Using eepp XML vocabulary, you can quickly design UI layouts and the screen
|
|
elements they contain, in the same way you create web pages in HTML — with a
|
|
series of nested elements.
|
|
|
|
The XML widget elements will be added recursively to the desired parent ( or
|
|
the root element of the UI ).
|
|
You can add additional layout objects or widgets as child elements to gradually
|
|
build a view hierarchy that defines your layout. For example, here's an XML
|
|
layout that uses a vertical EE::UI::UILinearLayout to hold a EE::UI::UITextView
|
|
and a EE::UI::UIPushButton:
|
|
|
|
```xml
|
|
<LinearLayout layout_width="match_parent"
|
|
layout_height="match_parent"
|
|
orientation="vertical" >
|
|
<TextView id="text_view"
|
|
layout_width="match_parent"
|
|
layout_height="wrap_content"
|
|
text="Hello, I am a TextView" />
|
|
<PushButton id="button_view"
|
|
layout_width="match_parent"
|
|
layout_height="wrap_content"
|
|
text="Hello, I am a Button" />
|
|
</LinearLayout>
|
|
```
|
|
|
|
After you've declared your layout in XML, save the file with the `.xml`
|
|
extension into a project accessible path and you're done.
|
|
|
|
## Initializing the UI
|
|
|
|
The library does not assume that the user is going to use the UI at all, so you
|
|
must initialize it manually. This usually comes right after the main
|
|
EE::Window::Window initialization.
|
|
|
|
The process consist in very few steps: Loading a default font. Instantiating the
|
|
EE::UI::UISceneNode. Setting the font as the default scene node. And finally,
|
|
adding this scene node to the EE::Scene::SceneManager.
|
|
|
|
For example:
|
|
|
|
```cpp
|
|
// ... after window creation
|
|
// Load a font to use as the default font in our UI.
|
|
FontTrueType* font =
|
|
FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" );
|
|
|
|
// Create a new scene node to add our widgets.
|
|
UISceneNode* uiSceneNode = UISceneNode::New();
|
|
|
|
// Set the default font used in the scene node (otherwise we won't have any font to create
|
|
// text views).
|
|
uiSceneNode->getUIThemeManager()->setDefaultFont( font );
|
|
|
|
// Add the new scene node to the scene manager.
|
|
SceneManager::instance()->add( uiSceneNode );
|
|
```
|
|
|
|
And that's all we need for a basic initialization.
|
|
|
|
## Updating the UI
|
|
|
|
Updating the UI consist in two simple steps: updating and drawing the UI scene
|
|
node contained by the EE::Scene::SceneManager.
|
|
|
|
So we just need to do in our main loop:
|
|
|
|
```cpp
|
|
// Update the UI scene
|
|
SceneManager::instance()->update();
|
|
|
|
// Check if the UI has been invalidated ( needs redraw )
|
|
if ( SceneManager::instance()->getUISceneNode()->invalidated() ) {
|
|
// Clear the window.
|
|
win->clear();
|
|
|
|
// Redraw the UI scene.
|
|
SceneManager::instance()->draw();
|
|
|
|
// Display the scene to the screen.
|
|
win->display();
|
|
} else {
|
|
// For for an input event or timeout after 16ms if has focus, otherwise wait 100ms.
|
|
win->getInput()->waitEvent( Milliseconds( win->hasFocus() ? 16 : 100 ) );
|
|
}
|
|
```
|
|
|
|
This example only redraws the scene when the EE::UI::UISceneNode indicates
|
|
that changes has been detected that needs to redraw the scene. You can redraw
|
|
the scene on every update or only when is needed. There are several options
|
|
regarding this topic, since each scene node can be drawn into a separated
|
|
frame buffer, in order to be able to control when to redraw a scene.
|
|
|
|
## Load the XML Layout Resource
|
|
|
|
XML layout resources can be loaded from any file system path,
|
|
EE::UI::Pack accessible path or a hard-coded string.
|
|
|
|
Layout resources will be appended to the EE::Scene::Node (the most basic element
|
|
in a EE::Scene::SceneNode) specified by the user, if no node is specified the
|
|
root scene node will be used.
|
|
|
|
Following the [Write the XML](#write-the-xml) layout and the
|
|
[Initializing the UI](#initializing-the-ui) code:
|
|
|
|
```cpp
|
|
uiSceneNode->loadLayoutFromString( R"xml(
|
|
<LinearLayout layout_width="match_parent"
|
|
layout_height="match_parent"
|
|
orientation="vertical">
|
|
<TextView id="text_view"
|
|
layout_width="match_parent"
|
|
layout_height="wrap_content"
|
|
text="Hello, I am a TextView" />
|
|
<PushButton id="button_view"
|
|
layout_width="match_parent"
|
|
layout_height="wrap_content"
|
|
text="Hello, I am a PushButton" />
|
|
</LinearLayout>
|
|
)xml" );
|
|
```
|
|
|
|
This will load and create the widgets into the root element of the UI scene
|
|
node.
|
|
|
|
## Cascading Style Sheets
|
|
|
|
[CSS (Cascading Style Sheets)](https://developer.mozilla.org/en-US/docs/Web/CSS)
|
|
is the code you use to style the UI. It's very heavily based on the
|
|
[CSS 2.1](https://www.w3.org/TR/CSS21/) standards with a good amount of CSS 3
|
|
features. Some of the main differences are: only properties explicitly registered as
|
|
inheritable pass their values to child widgets, and native layout properties follow eepp's
|
|
layout model rather than browser layout. eepp also adds properties for decoration and layout
|
|
control. See [CSS for Application UI](ui_css_for_applications.md) for the native cascade and
|
|
inheritance rules, and the [HTML compatibility guide](ui_html_compatibility.md) when styling
|
|
HTML content.
|
|
|
|
Important CSS3 features that are currently supported:
|
|
|
|
* [Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/)
|
|
|
|
* [Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions)
|
|
|
|
* [Custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
|
|
|
|
* [Media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries)
|
|
|
|
* [@font-face at rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face)
|
|
|
|
* [:root element](https://developer.mozilla.org/en-US/docs/Web/CSS/:root)
|
|
|
|
* [Most of the background properties](https://developer.mozilla.org/en-US/docs/Web/CSS/background)
|
|
|
|
See the [CSS Specification](css_specification.md) for more information.
|
|
|
|
## Write the CSS
|
|
|
|
Following [Load the XML Layout Resource](#load-the-xml-layout-resource) we are
|
|
going to write a very simple style to our UI. In order to that we can create a
|
|
new CSS file (with `.css` extension) in our file system and save it in an
|
|
accesible path to our project, or we can simply write the CSS in a string in our
|
|
code.
|
|
|
|
This is how our CSS could look like:
|
|
|
|
```css
|
|
* {
|
|
font-size: 22dp;
|
|
}
|
|
|
|
TextView {
|
|
background-color: white;
|
|
color: black;
|
|
text-align: center;
|
|
}
|
|
|
|
PushButton {
|
|
background-color: red;
|
|
color: white;
|
|
}
|
|
```
|
|
|
|
The only difference we can spot in this example with the CSS standard is that
|
|
eepp UI support a new CSS unit called "dp", representing a [Device-independent
|
|
pixel](https://en.wikipedia.org/wiki/Device-independent_pixel), that allow us to
|
|
keep our layout consistent between different screen densities. This concept was
|
|
taken from the [Android pixel density implementation](https://developer.android.com/guide/practices/screens_support.html#density).
|
|
|
|
## Loading the CSS Resource
|
|
|
|
Following [Load the XML Layout Resource](#load-the-xml-layout-resource) and
|
|
[Write the CSS](#write-the-css) we are going to load our CSS from a string:
|
|
|
|
```cpp
|
|
// We can also use "combineStyleSheet" to combine the new CSS with any previously set CSS.
|
|
uiSceneNode->setStyleSheet( R"css(
|
|
* {
|
|
font-size: 22dp;
|
|
}
|
|
|
|
TextView {
|
|
background-color: white;
|
|
color: black;
|
|
text-align: center;
|
|
}
|
|
|
|
PushButton {
|
|
background-color: red;
|
|
color: white;
|
|
}
|
|
)css" );
|
|
```
|
|
|
|
To load a CSS from a file we need to first parse it with the
|
|
EE::UI::CSS::StyleSheetParser.
|
|
|
|
It will look like:
|
|
|
|
```cpp
|
|
CSS::StyleSheetParser parser;
|
|
parser.loadFromFile( "path/to/our/stylesheet.css" );
|
|
uiSceneNode->setStyleSheet( parser.getStyleSheet() );
|
|
```
|
|
|
|
## Example
|
|
|
|
For a complete example of this introduction you can look into:
|
|
[src/examples/ui_hello_world/ui_hello_world.cpp](https://github.com/SpartanJ/eepp/blob/develop/src/examples/ui_hello_world/ui_hello_world.cpp).
|
|
|
|
Also is important to notice that for applications that wants to use the default eepp UI theme and fonts you can simply take advantage of the EE::UI::UIApplication class which controls the initialization and loading of the core components of the UI. You can look at the simplest example at [src/examples/ui_application_hello_world/ui_application_hello_world.cpp](https://github.com/SpartanJ/eepp/blob/develop/src/examples/ui_application_hello_world/ui_application_hello_world.cpp).
|
|
|
|
To run that application example, follow the [build instructions for your platform](../../README.md#how-to-build-it)
|
|
and build the `eepp-ui-application-hello-world` target, or build the full project. Its executable
|
|
is placed in `bin/` (`eepp-ui-application-hello-world.exe` on Windows). Run it from there.
|
|
|
|
The example creates its widgets from the XML string in that source file. Change the `TextView`'s
|
|
`text` attribute to see an XML edit. To try CSS, add this before `return app.run();`:
|
|
|
|
```cpp
|
|
app.getUI()->combineStyleSheet( R"css(
|
|
#text_view {
|
|
font-size: 28dp;
|
|
color: #1a73e8;
|
|
}
|
|
)css" );
|
|
```
|
|
|
|
Rebuild the example with the same platform build workflow, then run it again. The label should show
|
|
your new text in a larger blue font. Press **F11** in the example window to open the visual widget
|
|
inspector; select `text_view` or use **Pick Widget** to see its computed properties and matching
|
|
style rules. The inspector is implemented in
|
|
[`UIWidgetInspector`](../../include/eepp/ui/tools/uiwidgetinspector.hpp).
|
|
|
|
For applications with reactive state, typed input, validation, or observable collections, continue
|
|
with the [UI Data Binding](ui_databinding.md) guide.
|
|
|
|
For interactive line charts, live data, grids, and tooltip examples, see [UI Charts](ui_charts.md).
|
|
|
|
## Environment overrides
|
|
|
|
eepp UI applications accept these environment variables:
|
|
|
|
| Variable | Accepted values |
|
|
| --- | --- |
|
|
| `EEPP_FONT_HINTING` | `none`, `slight`, `full` |
|
|
| `EEPP_FONT_ANTIALIASING` | `none`, `grayscale`, `subpixel` |
|
|
| `EEPP_COLOR_SCHEME` | `light`, `dark`, `system` |
|
|
|
|
Valid environment values take precedence over application settings and command-line options.
|
|
Unrecognized values are ignored. `system` follows the operating system color scheme.
|