Frame UI - Kyoril/mmo GitHub Wiki
Frame UI is the library that is responsible for everything that has anything to do with the graphical user interface in the project. It can render text, frames with complex layouts and more. It also allows to write custom renderer classes, for example to render 3d models in frames. It completely relies on the graphics library, which makes it portable so that it should work out of the box with every implemented graphics api.
The key component is probably the FrameManager class, defined in the header file frame_ui/frame_mgr.h
. This is a static class, which has a static Get
method that you can use to access it's singleton instance. Before using it, you should call the static method FrameManager::Initialize()
for internal initializations and FrameManager::Destroy()
afterwards for destruction (when you're done using the FrameUI library, obviously). You also need to make sure that the graphics library is properly initialized before using Frame UI. In the client, this is done in the Console
class, implemented in the console.cpp
file.
Frame UI supports loading resources from xml files. It uses the AssetRegistry class of the assets
library to do so, so you need to make sure, the AssetRegistry is properly initialized before trying to load any files. Using the AssetRegistry enables a whole new set of features, like loading files from virtual files (archives), which is currently used in distributed Release builds of the client. Currently, there are three file types supported:
*.toc files are simple text files which can contain empty lines, comment lines (prefixed with a '#' sign) and text lines which may not start space characters at the moment. Each text line is considered a file name relative to the AssetRegistry's root directory. When loading a toc file, all referenced files are loaded in order. A load cycle check is also performed, to prevent infinitely loading the same file over and over again, which would ultimately result in a stack overflow. A toc file may reference every of the three supported file types, so a toc file may actually reference another toc file.
Lua files are script files that contain lua code. Right now, this is just a planned feature and not yet implemented. The idea is to have the ui logic be performed entirely by lua code, and that the calling library or application provide links for the ui lua context.
For example, a click on the Login button in the client will be handled in lua code, and then call a function that is provided by the client's c++ code to start the actual login process. The client will then trigger events in c++ that are again handled by lua to update the state of the ui (like disabling or hiding frames, showing new ones etc.).
Xml files are actually split into two types of xml files: Style Xml and Layout Xml. Which type a xml file is, is determined by the first (root) tag of the file, which has to be either UiStyle
for style xml, or UiLayout
for layout xml files.
Frame UI will automatically handle these in a static instance of the StyleXmlLoader
or LayoutXmlLoader
class, defined in frame_ui/layout_xml_loader.h
and frame_ui/style_xml_loader.h
respectively.
If you don't want or need to use xml, you can create the ui completely in c++. To do so, have a look at the xml loader classes mentioned above and how they make use of the StyleManager
and FrameManager
classes to create styles and frames.
However, since FrameUI is quite complex at this point, it is not really recommended to create frames and styles directly in c++, for obvious reasons.
One part that is used directly by c++ though, is the Font
class, defined in frame_ui/font.h
. This class, again, makes use of the AssetRegistry
class in the assets
library, so make sure it is initialized before using the Font
class.
The font class is quite useful for rendering text without using Frames, Layout and all that stuff. For example, it is currently used by the client's Console
class to render the console contents (see console.cpp
of the client).
It is also really easy to use: Just create a new std::shared_pointer<Font>
(also referenced as FontPtr
type, defined in frame_ui/font.h
) like this:
const int fontSize = 10;
const int outlineSizeInPixels = 1;
m_font = std::make_shared<Font>("name/of/fontfile.ttf", fontSize, outlineWidthInPixels);
Remember, that the filename of the ttf file is again relative to the root directory of AssetRegistry
.
Almost every element represented on the screen is a frame (base class: Frame
, defined in frame_ui/frame.h
). The Frame class itself only contains properties of a frame.
For most flexibility, FrameUI uses some sort of MVC (model, view, controller) model the frames. The frame would be the model in this example, containing the required data.
A style would be the view, describing how a control can be rendered.
A FrameRenderer
subclass would be the controller, which uses a frame and it's style to render the contents of a frame on screen. Currently, there are three FrameRenderer classes:
This is the abstract base class for a frame renderer and thus needs to be inherited. It is defined in frame_ui/frame_renderer.h
. The most important method to implement is FrameRenderer::Render(optional<Color> color = optional<Color>(), optional<Rect> clipper = optional<Rect>())
.
This is the most basic renderer class that draws a frame on screen. It works by determining a state name, depending on the frame's properties.
The default renderer uses the Enabled
state as default, and the Disabled
state if the frame's Frame::IsEnabled()
property returns false.
The renderer then uses the frame's style and tries to find a StateImagery
instance with the name of the active state. If no such StateImagery
exists, it tries to use the default state name (which for this class is Enabled
) and tries again. If still no such StateImagery
exists, nothing is rendered at all. It's the same if the frame does not have a valid style assigned: Nothing will be rendered then.
The ButtonRenderer
works exactly the same as the DefaultRenderer
, except that it sets different states. The states it sets are: Normal
(default and fallback), Hovered
(if Frame::IsHovered()
returns true), Pushed
(not yet implemented) and Disabled
(again, if Frame::IsEnabled()
returns false).
A Style
(frame_ui/style.h
) basically consists of a unique name, a number of uniquely named, case insensitive StateImagery
instances and a number of uniquely named, case insensitive ImagerySection
s.
An ImagerySection
(frame_ui/imagery_section.h
) contains a number of FrameComponent
s that will actually draw the contents of a frame.
There are multiple FrameComponent
classes defined, which are: FrameComponent
, ImageComponent
, TextComponent
and BorderComponent
.
TODO: Add more details about components in here
A StateImagery
(frame_ui/state_imagery.h
) contains a number of FrameLayer
instances, which are rendered in order.
A FrameLayer
(frame_ui/frame_layer.h
) contains a number of ImagerySection
references that are owned by the Style
instance. When asked to render, the FrameLayer
will call ImagerySection::Render(...)
for every section assigned, in assignment order, to make each section redirect the call to all it's FrameComponent
s, to actually render them.
So, the final render call for a frame, looks something like this:
Frame::Render()
> FrameRenderer::Render()
>> StateImagery::Render()
>>> FrameLayer::Render()
>>>> ImagerySection::Render()
>>>>> FrameComponent::Render()
>>>>> FrameComponent::Render()