Crate masonry

Crate masonry 

Source
Expand description

Masonry is a foundational framework for building GUI libraries in Rust.

The developers of Masonry are developing Xilem, a reactive UI library built on top of Masonry. Masonry’s API is geared towards creating GUI libraries; if you are creating an application, we recommend also considering Xilem.

Masonry gives you a platform-independent manager, which owns and maintains a widget tree. It also gives you tools to inspect that widget tree at runtime, write unit tests on it, and generally have an easier time debugging and maintaining your app.

The framework is not opinionated about what your user-facing abstraction will be: you can implement immediate-mode GUI, the Elm architecture, functional reactive GUI, etc., on top of Masonry.

It is opinionated about its internals: things like text focus, pointer interactions and accessibility events are often handled in a centralized way.

Masonry is built on top of:

Masonry can be used with any windowing library which allows the window content to be rendered using wgpu. There are currently two backends for using Masonry to create operating system windows:

§Example

The to-do-list example looks like this, using masonry_winit as the backend:

use masonry::core::{ErasedAction, NewWidget, Properties, Widget, WidgetId, WidgetTag};
use masonry::dpi::LogicalSize;
use masonry::peniko::color::AlphaColor;
use masonry::properties::Padding;
use masonry::properties::types::Length;
use masonry::theme::default_property_set;
use masonry::widgets::{Button, ButtonPress, Flex, Label, Portal, TextAction, TextArea, TextInput};
use masonry_winit::app::{AppDriver, DriverCtx, NewWindow, WindowId};
use masonry_winit::winit::window::Window;

const TEXT_INPUT_TAG: WidgetTag<TextInput> = WidgetTag::new("text-input");
const LIST_TAG: WidgetTag<Flex> = WidgetTag::new("list");
const WIDGET_SPACING: Length = Length::const_px(5.0);

struct Driver {
    next_task: String,
    window_id: WindowId,
}

impl AppDriver for Driver {
    fn on_action(
        &mut self,
        window_id: WindowId,
        ctx: &mut DriverCtx<'_, '_>,
        _widget_id: WidgetId,
        action: ErasedAction,
    ) {
        debug_assert_eq!(window_id, self.window_id, "unknown window");

        if action.is::<ButtonPress>() {
            let render_root = ctx.render_root(window_id);

            render_root.edit_widget_with_tag(TEXT_INPUT_TAG, |mut text_input| {
                let mut text_area = TextInput::text_mut(&mut text_input);
                TextArea::reset_text(&mut text_area, "");
            });
            render_root.edit_widget_with_tag(LIST_TAG, |mut list| {
                let child = Label::new(self.next_task.clone()).with_auto_id();
                Flex::add_child(&mut list, child);
            });
        } else if action.is::<TextAction>() {
            let action = action.downcast::<TextAction>().unwrap();
            match *action {
                TextAction::Changed(new_text) => {
                    self.next_task = new_text.clone();
                }
                TextAction::Entered(_) => {}
            }
        }
    }
}

/// Return initial to-do-list without items.
pub fn make_widget_tree() -> NewWidget<impl Widget> {
    let text_input = NewWidget::new_with_tag(
        TextInput::new("").with_placeholder("ex: 'Do the dishes', 'File my taxes', ..."),
        TEXT_INPUT_TAG,
    );
    let button = NewWidget::new(Button::with_text("Add task"));

    let list = Flex::column()
        .with_child(NewWidget::new_with_props(
            Flex::row()
                .with_flex_child(text_input, 1.0)
                .with_child(button),
            Properties::new().with(Padding::all(WIDGET_SPACING.get())),
        ))
        .with_spacer(WIDGET_SPACING);

    NewWidget::new(Portal::new(NewWidget::new_with_tag(list, LIST_TAG)))
}

fn main() {
    let window_size = LogicalSize::new(400.0, 400.0);
    let window_attributes = Window::default_attributes()
        .with_title("To-do list")
        .with_resizable(true)
        .with_min_inner_size(window_size);
    let driver = Driver {
        next_task: String::new(),
        window_id: WindowId::next(),
    };

    let event_loop = masonry_winit::app::EventLoop::with_user_event()
        .build()
        .unwrap();
    masonry_winit::app::run_with(
        event_loop,
        vec![
            NewWindow::new_with_id(
                driver.window_id,
                window_attributes,
                make_widget_tree().erased(),
            )
            .with_base_color(AlphaColor::from_rgb8(2, 6, 23)),
        ],
        driver,
        default_property_set(),
    )
    .unwrap();
}

Running this will open a window that looks like this:

Screenshot of the to-do-list example

§Feature flags

The following crate feature flags are available:

  • tracy: Enables creating output for the Tracy profiler using tracing-tracy. This can be used by installing Tracy and connecting to a Masonry with this feature enabled.
  • testing: Re-exports the test harness from masonry_testing.

§Debugging features

Masonry apps currently ship with several debugging features built in:

  • A rudimentary widget inspector - toggled by the F11 key.
  • A debug mode painting widget layout rectangles - toggled by the F12 key.
  • Optional automatic registration of a tracing subscriber, which outputs to the console and to a file in the dev profile.

If you want to use your own subscriber, simply set it before starting masonry - in this case masonry will not set a subscriber.

Re-exports§

pub use accesskit;
pub use vello::kurbo;
pub use vello::peniko;
pub use dpi;
pub use parley;
pub use vello;
pub use masonry_testing as testing;testing
pub use ui_events;

Modules§

app
Types needed for running a Masonry app.
core
Basic types and traits Masonry is built on.
doc
Documentation-only module
palette
Palettes with predefined colors.
properties
Types and logic commonly used across widgets.
theme
Default values used by various widgets in their paint methods.
util
Miscellaneous utility functions.
widgets
Common widgets.

Structs§

TextAlignOptions
Additional options to fine tune alignment

Enums§

TextAlign
Alignment of a layout.