NoesisGUI

Application Framework

github Tutorial Data

ApplicationTutorialImg1.jpg

Although noesisGUI can be fully integrated within your application, as shown in the integration tutorial, an Application Framework is also provided. This framework is an Open Source library that provides abstractions to create multiplatform applications. The framework is a good example about how to use Noesis and it provides many helpers that can be useful. All our public samples use the application framework.

The extensions provided, like Application and Window, are compatible with WPF, so compatible with Microsoft Blend. Using the application framework you can design applications that will indistinctly run in a Windows window, macOS window, iPhone screen, or Xbox TV for example. The framework supports all the platforms where NoesisGUI is available.

spacer.png

The following is a list of the functionality exposed by the Application Framework:

  • Multiplatform abstractions for Application and Window.
  • Multiplatform handling of input events: keyboard, mouse, touch and gamepads.
  • Support for playing sounds.
  • Resource Providers implementation for loading assets.
  • Render implementations for each supported graphics API.
  • The Interactivity package.

NOTE

Application Framework provides the functionality needed by our samples and tutorials. It should be never used as a 'Production Ready' library. Clients should take the code as a starting point that must be adapted and tweaked for each specific scenario.

Headers and Namespace

Headers for the application framework are contained within the NsApp and NsRender modules. The API is exposed in the NoesisApp namespace.

#include <NsApp/EntryPoint.h>
#include <NsApp/Application.h>
#include <NsApp/ApplicationLauncher.h>
#include <NsApp/Window.h>
#include <NsApp/EmbeddedXamlProvider.h>
#include <NsApp/EmbeddedFontProvider.h>

using namespace Noesis;
using namespace NoesisApp;

NOTE

Note how framework headers are not inside standard NoesisGUI include folder neither part of 'Noesis_pch.h' precompiled header.

Entry Point

As main() is not available in all platforms, the framework provides a portable entry point, NsMain. You will normally create here a Launcher, configure it and Run() it.

int NsMain(int argc, char **argv)
{
    AppLauncher launcher;
    launcher.SetArguments(argc, argv);
    launcher.SetApplicationFile("App.xaml");
    return launcher.Run();
}

Launcher

The Launcher is the first instance you create. It is in charge of application initialization and main message loop. It also provides a few important overridable functions:

  • RegisterComponents: for registering classes in the component factory. Each extended class that need to be instantiated by XAML needs to be registered here as explained in the Extending NoesisGUI tutorial. For the framework you need to register at least the Application and Window class.
  • GetXamlProvider, GetFontProvider and GetTextureProvider: for customizing how resources are loaded. You can install custom handlers for XAMLs, font and textures as explained in the Customizing Resource Loading tutorial.
class AppLauncher final: public ApplicationLauncher
{
private:
    void RegisterComponents() const override
    {
        NsRegisterComponent<RssReader::App>();
        NsRegisterComponent<RssReader::MainWindow>();
    }

    Ptr<XamlProvider> GetXamlProvider() const override
    {
        EmbeddedXaml xamls[] =
        {
            { "App.xaml", App_xaml, sizeof(App_xaml) },
            { "MainWindow.xaml", MainWindow_xaml, sizeof(MainWindow_xaml) }
        };

        return *new EmbeddedXamlProvider(xamls, NS_COUNTOF(xamls));
    }

    Ptr<FontProvider> GetFontProvider() const override
    {
        EmbeddedFont fonts[] =
        {
            { "", Roboto_Regular_ttf, sizeof(Roboto_Regular_ttf) },
            { "", Roboto_Bold_ttf, sizeof(Roboto_Bold_ttf) }
        };

        return *new EmbeddedFontProvider(fonts, NS_COUNTOF(fonts));
    }
};

NOTE

The Embedded* family of resource providers found in the framework allows you to embed resources in the executable. Each time you edit a resource contained in the project it is automatically converted to a header file using 'bin2h', an tool distributed with each sample. For example, 'Roboto-Regular.ttf.bin.h' is automatically regenerated each time you touch ''Roboto-Regular.ttf'. The same for the rest of resources.

Application

The Application class directly correspond to the WPF Application Class. Each application provides its own implementation inheriting from Application base class.

namespace RssReader
{
    class App final: public Application
    {
        NS_IMPLEMENT_INLINE_REFLECTION(App, Application)
        {
            NsMeta<TypeId>("RssReader.App");
        }
    };
}

The main purpose of the Application is providing a global resource dictionary and setting the main window, the StartupUri.

App.xaml
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             x:Class="RssReader.App">

  <Application.Resources>
    <SolidColorBrush x:Key="Background0" Color="#FF2F3F4F" />
    <SolidColorBrush x:Key="Background1" Color="SlateGray" />
    <SolidColorBrush x:Key="Foreground0" Color="White" />
    <SolidColorBrush x:Key="Foreground1" Color="SkyBlue" />
    <SolidColorBrush x:Key="Foreground2" Color="Black" />
    <SolidColorBrush x:Key="Border" Color="LightSlateGray" />
    <FontFamily x:Key="DefaultFont">./#Roboto</FontFamily>
  </Application.Resources>

</Application>

Window

The Window class mimics the WPF Window Class. It is a container that shows its content inside an operating system window. Public properties like Title, ResizeMode, WindowStyle, Width and Height control the aspect of the native window.

MainWindow.xaml
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="NoesisGUI - RSS Reader" Width="487" Height="630" ResizeMode="NoResize"
   Background="{StaticResource Background0}"
   Foreground="{StaticResource Foreground0}"
   FontFamily="{StaticResource DefaultFont}"
   x:Class="RssReader.MainWindow">

  <Viewbox>
    <DockPanel Background="{StaticResource Background0}" LastChildFill="True"
               KeyboardNavigation.TabNavigation="Contained"
               KeyboardNavigation.DirectionalNavigation="Contained" Width="325" Height="420">
      <TextBlock DockPanel.Dock="Top" Text="RSS Reader" FontSize="32" FontWeight="Bold"
                 TextAlignment="Center" Margin="0,10,0,0"/>
      <Border DockPanel.Dock="Top" Background="{StaticResource Background1}"
              BorderBrush="{StaticResource Border}" BorderThickness="1" CornerRadius="2"
              Margin="10" Padding="15,5">
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Column="0" Text="URL: " VerticalAlignment="Center"/>
          <TextBox x:Name="Address" Grid.Column="1" Text="http://www.metacritic.com/"/>
          <Button x:Name="GoTo" Grid.Column="2" Content="Go" Click="OnGoToClicked" Margin="2,0,0,0"/>
        </Grid>
      </Border>
      <Grid DockPanel.Dock="Bottom" Margin="10">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.Resources>
          <Style TargetType="Button">
            <Setter Property="BorderBrush" Value="{StaticResource Background1}"/>
          </Style>
        </Grid.Resources>
        <Button x:Name="Prev" Grid.Column="0" Content="Prev" Click="OnPrevClicked"/>
        <Button x:Name="Next" Grid.Column="1" Content="Next" Click="OnNextClicked"/>
      </Grid>
      <Border Background="{StaticResource Background1}" BorderBrush="{StaticResource Border}"
              BorderThickness="1" CornerRadius="2" Margin="10,0" Padding="5">
        <Grid x:Name="ContentPanel" Margin="10,0">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
          </Grid.RowDefinitions>
          <TextBlock x:Name="EntryTitle" Grid.Row="0" FontSize="20" FontWeight="Bold"
                     Foreground="{StaticResource Foreground1}" TextAlignment="Center" Margin="0,5,0,5"/>
          <ScrollViewer Grid.Row="1"  Margin="0,10" Focusable="False"
                        HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
            <TextBlock x:Name="EntryDesc" TextWrapping="Wrap" Margin="10,0"
                       Foreground="{StaticResource Foreground2}" FontSize="14"/>
          </ScrollViewer>
        </Grid>
      </Border>
    </DockPanel>
  </Viewbox>

</Window>

Window base class is normally extended to implement code-behind functionality.

namespace RssReader
{
    class MainWindow final: public Window
    {
    public:
        MainWindow(): _index(0)
        {
            InitializeComponent();

            _title = FindName<TextBlock>("EntryTitle");
            _title->SetText(gTitles[0]);

            _desc = FindName<TextBlock>("EntryDesc");
            _desc->SetText(gBodies[0]);
        }

    private:
        void InitializeComponent()
        {
            Noesis::GUI::LoadComponent(this, "MainWindow.xaml");
        }

        bool ConnectEvent(BaseComponent* source, const char* event, const char* handler) override
        {
            NS_CONNECT_EVENT(Button, Click, OnGoToClicked);
            NS_CONNECT_EVENT(Button, Click, OnPrevClicked);
            NS_CONNECT_EVENT(Button, Click, OnNextClicked);
            return false;
        }

        void OnGoToClicked(BaseComponent* /*sender*/, const RoutedEventArgs& /*e*/)
        {
        }

        void OnPrevClicked(BaseComponent* /*sender*/, const RoutedEventArgs& /*e*/)
        {
            _index = _index == 0 ? 2 : _index - 1;
            _title->SetText(gTitles[_index]);
            _desc->SetText(gBodies[_index]);
        }

        void OnNextClicked(BaseComponent* /*sender*/, const RoutedEventArgs& /*e*/)
        {
            _index = _index == 2 ? 0 : _index + 1;
            _title->SetText(gTitles[_index]);
            _desc->SetText(gBodies[_index]);
        }

    private:
        int _index;
        TextBlock* _title;
        TextBlock* _desc;

        NS_IMPLEMENT_INLINE_REFLECTION(MainWindow, Window)
        {
            NsMeta<TypeId>("RssReader.MainWindow");
        }
    };
}

Command Line Switches

The following command line switches are supported by all applications created with the Application Framework:

  • --render [D3D11|GL|Metal|...]: overrides the default renderer.
  • --vsync [0|1]: disables vertical synchronization.
  • --samples N: enables multisample anti-aliasing (MSAA), by default it is off.
  • --linear: for switching to linear rendering, by default rendering happens in gamma space.
  • --log_binding: to increase the verbosity of logging when using data binding.

Shortcut Keys

The following shortcuts enable debugging functionality to inspect the performance of your application:

  • CTRL + W: toggles wireframe mode when rendering triangles.
  • CTRL + B: each batch submitted to the GPU is given a unique solid color.
  • CTRL + O: display pixel overdraw using blending layers. Different colors are used for each type of triangle: green for normal, red for opacities and blue for clipping masks.
  • CTRL + P: per-primitive Antialiasing extrudes the contours of the geometry and smooths them. Useful when GPU multisampling is not enabled.
  • CTRL + F: display a performance stats panel.
© 2017 Noesis Technologies