Giter Club home page Giter Club logo

surreal's Introduction

Surreal: Simple Rust User Interface Library

Note

Surreal originated as a research project. The corresponding paper is currently in process.

Surreal exists to explore declarative user interface design patterns.
As it stands, Surreal is usable, but lacks many features.

Please see the Project tab for a list of todos. There are also many such todos throughout the Surreal codebase as well as notes for future work.

For a working example, see examples/test.rs which demonstrates all of Surreal's current capabilities.

About

Surreal is a Rust-native user interface library focused on simplicity and performance. Below is an example demonstrating the simple, declarative style of Surreal:

use surreal::*;

// If you don't need messages, they can be left out. The message type will simply become `()`

#[derive(EmptyMessage)] // This trait prevents the message queue from accepting empty messages
#[empty(None)] // This attribute specifies which variant is "empty" (no message)
enum Message {
    UpdateCounterText,
    None,
}

fn main() {
    // Views use declarative syntax
    // The `Stateful!` macro adds custom syntax for working with shared state
    let mut view = Stateful! {
        // Procedural macro magic happens here
        @State {
            counter: i32 = 0,
        },

        // `@counter` is compiled to `*state.get::<i32>("counter")`
        VStack! { 
            Text::new("counter_text")
                // Message handlers have access to their owners such as this very Text widget
                .message_handler(|this, message, mut state| {
                    if let Message::UpdateCounterText = message {
                        this.set_text(@counter.to_string());
                    }
                })
                // Fonts are specified by an alias (see `include_fonts!` below)
                .font("bold"),

            HStack! {
                Button::new("increment")
                    // Note that nested widgets don't need ids, but they can help with debugging
                    .text(Text::new("") 
                        .text("+")
                    )
                    // Elements can send messages to communicate with other elements
                    .on_click(|mut state| {
                        @counter += 1;
                        Message::UpdateCounterText
                    })
                    // Buttons can be rounded using a percentage (0% to 100%)
                    .roundness(100.0),

                CircleButton::new("decrement")
                    // CircleButtons can contain either a character or an image resource
                    .character(Text::character('-'))
                    // If closures become too large, simply pass in a function
                    .on_click(|mut state| {
                        @counter -= 1;
                        Message::UpdateCounterText
                    }),
            },
        }
        // If the theme's default alignment isn't centered, this will make the VStack centered, but not change the nested HStack
        .alignment(Alignment::Center) 
    };

    // Generates a map of fonts with given aliases
    let fonts = include_fonts! {
        // "default" alias is required. All text will use this by default.
        default => "../default_font_path",
        bold    => "../bold_font_path",
    };

    // Specify window and application settings
     let app = Application::new(ApplicationSettings {
        title: "Surreal Example",
        fonts,
        ..Default::default()
    });

    app.run(&mut view);
}

Building

Surreal currently works with the following platforms:

  • Windows
  • Linux
  • Android
  • Web (unstable)
  • Mac (untested)
  • iOS (untested)

Android Builds
How to build wgpu-rs + winit apps for Android:

  1. Install the sdkmanager tool. See this for help

  2. Install the build tools and platform tools using sdkmanager

  3. Install a compatible SDK and NDK version using sdkmanager

  4. Install cargo-apk via cargo install cargo-apk

  5. Follow the android-ndk-rs guide to configure your application's Android build (also see ./examples/android.rs)

  6. Build the app using cargo apk build (your build flags here)

  7. You may get various warnings. Check your_app/target/debug/apk for your_app.apk before taking any action

  8. Sign your app using the following JDK commands:

    • ensure your JDK path is exported (mine is C:\Program Files\Java\jdk-10\bin)
    • keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
      • This command does not need to be used each time
    • jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore your_app.apk alias_name

    You can also look into apksigner.jar located in your_android_path/build-tools/your_version/lib

  9. Connect your developer-enabled device and run adb install "path/to/your_app.apk"

  10. View console output with adb logcat RustStdoutStderr:D *:S (will also output panics, etc.)

Customizability

Surreal offers two means of customizing the appearance of an application:

Global Themes

A global theme defines the default style for all view elements. This includes colors, shapes, fonts, padding, and layouts.

See src/style.rs for available styling options.

TODO: Themes are declared using struct-like syntax:

@Theme: "Example theme"

colors: Colors {
    primary: Color::DARK_GRAY // Buttons default to this color
    secondary: Color::ALMOST_WHITE
    ...
},

text: Text {
    scale: 50.0, // Text will default to this size
    ...
}

Widget Style

The global theme can be overriden on a per-element basis. Each view element features builder functions for changing style. For example, the following will ignore the global theme's button color and text scale:

Button::new("using_specific_color")
    .color(Colors::WHITE) // Would otherwise use theme's button color
    .text(Text::new("")
        .text("This text has a specific size")
        .scale(100.0) // Would otherwise use theme's text scale
    )

Application Logic

Two interfaces exist for application logic. These can be used together, or alone.
At the center of these are message--a user defined type sent out by various UI interactions.

Messages

Messages are sent by widgets like so:

Widget::new("id")
    .on_some_action(|mut state| {
        @state_field.do_something();
        Message::ResponseToAction
    }),

Message Handlers

Message handlers can be attached to widgets. These accept the widget, the message to handle, and the application state:

Widget::new("id")
    .message_handler(|this, message, mut state| {
        match message {
            MessageType::Variant { optional_data } => {
                this.do_something();
            }
            ...
        }
    }),

View Hooks

View hooks are special in that they gain access to the view itself. This allows for element lookup by id, allowing hooks to effectively replace message handlers.

Additionally, hooks can be used to append and delete widgets within views:

let view = ...;

view.set_hook(|this, message| {
    match message {
        MessageType::Variant1 => {
            // Get a widget and cast it to the appropriate type
            GetWidget!(view.text_id as Text);
                .set_text("Set from hook");

            // Has access to the view
            this.do_something();
        }

        MessageType::Variant2 => {
            // Append a widget to the view
            this.append(Widget::new("id"));
            // Remove widget from view
            this.delete("id");
        }
        ...
    }
});

Control Flow (TODO: Outdated)

  • Initialization:
    • View::_init -> View::init + Widget::init -> View::layout
  • Main Loop:
    • Event Loop (generates messages):
      • View::propogate_event -> Widget::handle_event
    • Message Loop (signals resize):
      • View::call_hook -> View::propogate_message -> Widget::handle_message
    • If should_resize:
      • repeat Initialization
    • Render:
      • View::render + Widget::render

surreal's People

Contributors

swerdloj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

isgasho foow

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.