Skip to content

Commit

Permalink
Import Masonry crate (#203)
Browse files Browse the repository at this point in the history
This commit adds the `masonry` crate as-is to the repository. It doesn't integrate Xilem with Masonry in any way.

Masonry is a modular backend providing a Widget tree for GUI libraries, originally forked from the Druid project.

Previous history of this code is maintained in [the original Masonry repository](https://github.com/linebender/masonry). Explanations for design decisions may also be found in issues from [the Druid repository](https://github.com/linebender/druid).

---------

Co-authored-by: 7k5x <[email protected]>
Co-authored-by: 8176135 <[email protected]>
Co-authored-by: Aaron Muir Hamilton <[email protected]>
Co-authored-by: Alberto Slavica <[email protected]>
Co-authored-by: Al M <[email protected]>
Co-authored-by: Alve™ <[email protected]>
Co-authored-by: Andrea Cognolato <[email protected]>
Co-authored-by: Andrew Hickman <[email protected]>
Co-authored-by: Andrey Kabylin <[email protected]>
Co-authored-by: Andrii Lysenko <[email protected]>
Co-authored-by: Andrii Zymohliad <[email protected]>
Co-authored-by: Andrii Zymohliad <[email protected]>
Co-authored-by: Ankur Sethi <[email protected]>
Co-authored-by: Anna Scholtz <[email protected]>
Co-authored-by: Arif Driessen <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
Co-authored-by: arthmis <[email protected]>
Co-authored-by: astrale-sharp <[email protected]>
Co-authored-by: becky <[email protected]>
Co-authored-by: Benedikt Terhechte <[email protected]>
Co-authored-by: Benjamin Saunders <[email protected]>
Co-authored-by: binomial0 <[email protected]>
Co-authored-by: bitec0de <[email protected]>
Co-authored-by: bobtwinkles <[email protected]>
Co-authored-by: bobtwinkles <[email protected]>
Co-authored-by: Brook Heisler <[email protected]>
Co-authored-by: Brook Heisler <[email protected]>
Co-authored-by: Bruce Mitchener <[email protected]>
Co-authored-by: Bruno Dupuis <[email protected]>
Co-authored-by: Calle Malmz <[email protected]>
Co-authored-by: Carl Malmz <[email protected]>
Co-authored-by: ccQpein <[email protected]>
Co-authored-by: Chad Brokaw <[email protected]>
Co-authored-by: Charles Saracco <[email protected]>
Co-authored-by: Chris Overcash <[email protected]>
Co-authored-by: Christian Pérez-Llamas <[email protected]>
Co-authored-by: Christopher Anderson <[email protected]>
Co-authored-by: Christopher N. Hesse <[email protected]>
Co-authored-by: Christopher Serr <[email protected]>
Co-authored-by: Christoph Herzog <[email protected]>
Co-authored-by: Christoph <[email protected]>
Co-authored-by: Christoph <[email protected]>
Co-authored-by: colinfruit <[email protected]>
Co-authored-by: Colin Rofls <[email protected]>
Co-authored-by: Connor Brewster <[email protected]>
Co-authored-by: crumblingstatue <[email protected]>
Co-authored-by: Daniel Dulaney <[email protected]>
Co-authored-by: Daniel Kessler <[email protected]>
Co-authored-by: Daniel McNab <[email protected]>
Co-authored-by: Dan Knutson <[email protected]>
Co-authored-by: David Soria Parra <[email protected]>
Co-authored-by: David Soria Parra <[email protected]>
Co-authored-by: DetaNexus <[email protected]>
Co-authored-by: Diggory Hardy <[email protected]>
Co-authored-by: Dmitry Borodin <[email protected]>
Co-authored-by: Dmitry Borodin <[email protected]>
Co-authored-by: Dmitry Borodin <[email protected]>
Co-authored-by: Downtime <[email protected]>
Co-authored-by: Downtime <[email protected]>
Co-authored-by: DrGabble <[email protected]>
Co-authored-by: DUDDINO RITARDATO <[email protected]>
Co-authored-by: Edwin Cheng <[email protected]>
Co-authored-by: Edwin Cheng <[email protected]>
Co-authored-by: Egor Larionov <[email protected]>
Co-authored-by: Emilio Gonzalez <[email protected]>
Co-authored-by: Emilio González <[email protected]>
Co-authored-by: Erik Gilling <[email protected]>
Co-authored-by: Felix Rabe <[email protected]>
Co-authored-by: ForLoveOfCats <[email protected]>
Co-authored-by: ForLoveOfCats <[email protected]>
Co-authored-by: Gagandeep Singh <[email protected]>
Co-authored-by: Gagandeep Singh <[email protected]>
Co-authored-by: Garrett Risley <[email protected]>
Co-authored-by: GitHub <[email protected]>
Co-authored-by: gmorenz <[email protected]>
Co-authored-by: Greg Morenz <[email protected]>
Co-authored-by: Gyusun Yeom <[email protected]>
Co-authored-by: hammsvietro <[email protected]>
Co-authored-by: Hampus Lidin <[email protected]>
Co-authored-by: Harry <[email protected]>
Co-authored-by: Hilmar Gústafsson <[email protected]>
Co-authored-by: Hilmar Gústafsson <[email protected]>
Co-authored-by: HoNile <[email protected]>
Co-authored-by: HuszarG <[email protected]>
Co-authored-by: i509VCB <[email protected]>
Co-authored-by: Isaac Snow <[email protected]>
Co-authored-by: jaap aarts <[email protected]>
Co-authored-by: Jaap Aarts <[email protected]>
Co-authored-by: Jacob Zhong <[email protected]>
Co-authored-by: James Laver <[email protected]>
Co-authored-by: Jan Pochyla <[email protected]>
Co-authored-by: Jari Pennanen <[email protected]>
Co-authored-by: Jarrett Billingsley <[email protected]>
Co-authored-by: Jeff Muizelaar <[email protected]>
Co-authored-by: Jeff Parsons <[email protected]>
Co-authored-by: Jerboas86 <[email protected]>
Co-authored-by: Jerome Humbert <[email protected]>
Co-authored-by: jjl <[email protected]>
Co-authored-by: jneem <[email protected]>
Co-authored-by: Joe Neeman <[email protected]>
Co-authored-by: John Kåre Alsaker <[email protected]>
Co-authored-by: John Skottis <[email protected]>
Co-authored-by: John Terracina <[email protected]>
Co-authored-by: Jonas Platte <[email protected]>
Co-authored-by: Joshua Wade <[email protected]>
Co-authored-by: Joshua Wade <[email protected]>
Co-authored-by: joshuawade <[email protected]>
Co-authored-by: Justin Moon <[email protected]>
Co-authored-by: Kaiyin Zhong <[email protected]>
Co-authored-by: Kartoffelsaft <[email protected]>
Co-authored-by: Kaur Kuut <[email protected]>
Co-authored-by: kbalt <[email protected]>
Co-authored-by: keenz <[email protected]>
Co-authored-by: Keith Simmons <[email protected]>
Co-authored-by: kud1ing <[email protected]>
Co-authored-by: Lassi Pulkkinen <[email protected]>
Co-authored-by: lazypassion <[email protected]>
Co-authored-by: Lejero <[email protected]>
Co-authored-by: Leopold Luley <[email protected]>
Co-authored-by: Léo Stefanesco <[email protected]>
Co-authored-by: Lev Khoroshansky <[email protected]>
Co-authored-by: Lisael <[email protected]>
Co-authored-by: longmathemagician <[email protected]>
Co-authored-by: lord <[email protected]>
Co-authored-by: Maan2003 <[email protected]>
Co-authored-by: Maan2003 <[email protected]>
Co-authored-by: Maan2003 <[email protected]>
Co-authored-by: Manmeet Maan <[email protected]>
Co-authored-by: Manmeet Maan <[email protected]>
Co-authored-by: Manmeet <[email protected]>
Co-authored-by: Manmeet Singh <[email protected]>
Co-authored-by: Markus Kohlhase <[email protected]>
Co-authored-by: mastfissh <[email protected]>
Co-authored-by: Mathias Peters <[email protected]>
Co-authored-by: matt rice <[email protected]>
Co-authored-by: Maurizio Zucchelli <[email protected]>
Co-authored-by: Maximilian Köstler <[email protected]>
Co-authored-by: Melanie Riise <[email protected]>
Co-authored-by: Mendelt Siebenga <[email protected]>
Co-authored-by: MGlolenstine <[email protected]>
Co-authored-by: mobile-bungalow <[email protected]>
Co-authored-by: Moses Miller <[email protected]>
Co-authored-by: msiglreith <[email protected]>
Co-authored-by: Nico Burns <[email protected]>
Co-authored-by: Nils Martel <[email protected]>
Co-authored-by: Nils Martel <[email protected]>
Co-authored-by: Ofek Lev <[email protected]>
Co-authored-by: Olivier FAURE <[email protected]>
Co-authored-by: Olivier FAURE <[email protected]>
Co-authored-by: Paul Miller <[email protected]>
Co-authored-by: Paul Miller <[email protected]>
Co-authored-by: Pedro Hamms <[email protected]>
Co-authored-by: Per Lindgren <[email protected]>
Co-authored-by: Per <Per Lindgren>
Co-authored-by: Philipp Mildenberger <[email protected]>
Co-authored-by: Points Waves <[email protected]>
Co-authored-by: Psykopear <[email protected]>
Co-authored-by: pyroxymat <[email protected]>
Co-authored-by: pyroxymat <[email protected]>
Co-authored-by: Raph Levien <[email protected]>
Co-authored-by: Raph Levien <[email protected]>
Co-authored-by: raphlinus <[email protected]>
Co-authored-by: ratmice <[email protected]>
Co-authored-by: rhzk <[email protected]>
Co-authored-by: Richard Dodd (dodj) <[email protected]>
Co-authored-by: Richard Dodd <[email protected]>
Co-authored-by: Richard Poole <[email protected]>
Co-authored-by: rml <[email protected]>
Co-authored-by: Robert Lord <[email protected]>
Co-authored-by: Robert Wittams <[email protected]>
Co-authored-by: Roman Frołow <[email protected]>
Co-authored-by: Roman Zaynetdinov <[email protected]>
Co-authored-by: Roman Zaynetdinov <[email protected]>
Co-authored-by: Rose Hudson <[email protected]>
Co-authored-by: Ross Schulman <[email protected]>
Co-authored-by: Ruslan Kovtun <[email protected]>
Co-authored-by: Ruslan Prakapchuk <[email protected]>
Co-authored-by: Ryan Levick <[email protected]>
Co-authored-by: Ryan Levick <[email protected]>
Co-authored-by: sergej jurecko <[email protected]>
Co-authored-by: Shohei Ueda <[email protected]>
Co-authored-by: sigaloid <[email protected]>
Co-authored-by: sombrastudios <[email protected]>
Co-authored-by: Stanislav Lapata <[email protected]>
Co-authored-by: Starfighter <[email protected]>
Co-authored-by: Steven Malis <[email protected]>
Co-authored-by: Steven <[email protected]>
Co-authored-by: Steven Van Bael <[email protected]>
Co-authored-by: Sujit Joshi <[email protected]>
Co-authored-by: tay64 <[email protected]>
Co-authored-by: Ted de Munnik <[email protected]>
Co-authored-by: thecodewarrior <[email protected]>
Co-authored-by: Thierry Arnoux <[email protected]>
Co-authored-by: Thorbjørn Lindeijer <[email protected]>
Co-authored-by: Tim Schumacher <[email protected]>
Co-authored-by: tirix <[email protected]>
Co-authored-by: Tomasz Duda <[email protected]>
Co-authored-by: Tom Churchman <[email protected]>
Co-authored-by: totsteps <[email protected]>
Co-authored-by: totsteps <[email protected]>
Co-authored-by: Trent <[email protected]>
Co-authored-by: Uli Schlachter <[email protected]>
Co-authored-by: Uli Schlachter <[email protected]>
Co-authored-by: Valentin Kahl <[email protected]>
Co-authored-by: vishalsodani <[email protected]>
Co-authored-by: Vishal Sodani <[email protected]>
Co-authored-by: Walther Chen <[email protected]>
Co-authored-by: WeetHet <[email protected]>
Co-authored-by: WeetHet <[email protected]>
Co-authored-by: Wicher Heldring <[email protected]>
Co-authored-by: xarvic <[email protected]>
Co-authored-by: xarvic <[email protected]>
Co-authored-by: Zarenor Darkstalker <[email protected]>
Co-authored-by: Zoxc <[email protected]>
Co-authored-by: Zoxc <[email protected]>
  • Loading branch information
Show file tree
Hide file tree
Showing 141 changed files with 18,352 additions and 0 deletions.
9 changes: 9 additions & 0 deletions crates/masonry/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[target.'cfg(all())']
# TODO - Replace with Cargo.toml config
rustflags = [
# Global lints/warnings.
# We do this here instead of in the crate root because we want to apply
# these to example and test targets as well.
"-Aclippy::single_match",
"-Aclippy::bool_assert_comparison",
]
126 changes: 126 additions & 0 deletions crates/masonry/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
on:
push:
branches:
- master
pull_request:

env:
SKIP_RENDER_SNAPSHOTS: 1

jobs:
rustfmt:
runs-on: ubuntu-latest
name: cargo fmt
steps:
- uses: actions/checkout@v2

- name: install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
components: rustfmt
override: true

- name: cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

test-stable:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macOS-latest, windows-2019, ubuntu-latest]
name: cargo clippy+test
steps:
- uses: actions/checkout@v2

- name: install libx11-dev
run: |
sudo apt update
sudo apt install libx11-dev libpango1.0-dev libxkbcommon-dev libxkbcommon-x11-dev
if: contains(matrix.os, 'ubuntu')

- name: install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
profile: minimal
override: true

- name: restore cache
uses: Swatinem/rust-cache@v2

- name: cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --no-default-features

# We use --all-targets to skip doc tests; we run them in a parallel task
- name: cargo test
uses: actions-rs/cargo@v1
with:
command: test
args: --all-targets --no-default-features

doctest-stable:
runs-on: macOS-latest
name: doctests
steps:
- uses: actions/checkout@v2

- name: install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true

- name: restore cache
uses: Swatinem/rust-cache@v2

- name: cargo test --doc
uses: actions-rs/cargo@v1
with:
command: test
args: --doc --no-default-features

# This tests the future rust compiler to catch errors ahead of time without
# breaking CI
# We only run on a single OS to save time; this might let some errors go
# undetected until the compiler updates and they break CI; but that should
# happen rarely, and not pose too much of a problem when it does.
test-beta:
runs-on: macOS-latest
name: cargo clippy+check beta
steps:
- uses: actions/checkout@v2

- name: install beta toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: beta
components: clippy
profile: minimal
override: true

- name: restore cache
uses: Swatinem/rust-cache@v2

- name: cargo check
uses: actions-rs/cargo@v1
with:
command: check
args: --no-default-features
continue-on-error: true

- name: cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --no-default-features
continue-on-error: true
35 changes: 35 additions & 0 deletions crates/masonry/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Rust build files
target/

## Intellij Idea
.idea/

## Visual Studio Code
.vscode/

## Sublime Text
*.sublime-workspace
*.sublime-project

## Vim
tags
Session.vim
Sessionx.vim

## macOS
.DS_Store
.AppleDouble
.LSOverride

## Windows
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
*.lnk

## Linux
*~

# Visual snapshots
**/screenshots/*.diff.png
**/screenshots/*.new.png## Rust
168 changes: 168 additions & 0 deletions crates/masonry/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# ARCHITECTURE

Masonry is a framework that aims to provide the foundation for Rust GUI libraries.

Developers trying to write immediate-mode GUIs, Elm-architecture GUIs, functional reactive GUIs, etc, can import Masonry and get a platform to create windows (using Glazier as a backend) each with a tree of widgets. Each widget has to implement the Widget trait that Masonry provides.

This crate was originally a fork of Druid that emerged from discussions I had with Raph Levien and Colin Rofls about what it would look like to turn Druid into a foundational library. This means the code looks very similar to Druid's code and has mostly the same dependencies and primitives.


## High-level goals

Masonry has some opinionated design goals:

- **Be dumb.** As a general rule, Masonry doesn't do "algorithms". It has no reconciliation logic, no behind-the-scenes dataflow, no clever optimizations, etc. It tries to be efficient, but that efficiency comes from well-designed interfaces and well-placed abstraction boundaries. High-level logic should be implemented in downstream crates.
- **No mutability tricks.** Masonry tries to use as little unsafe code and cells/mutexes as possible. It's designed to work within Rust's ownership system, not to bypass it.
- **Facilitate testing.** Masonry implements a `TestHarness` type that helps users write unit tests, including tests with simulated user interactions and screenshot tests. In general, every feature should be designed with easy-to-write high-reliability tests in mind.
- **Facilitate debugging.** GUI app bugs are often easy to fix, but extremely painful to track down. GUI framework bugs are worse. Masonry should facilitate reproducing bugs and pinpointing which bit of code they come from.
- **Provide reflection.** Masonry should help developers surface some of the inherent structure in GUI programs. It should provide tools out-of-the-box to get information about the widget tree, performance indicators, etc. It should also provide accessibility data out-of-the-box.


## Code layout

### `src/platform/`

Some platform-specific stuff, for interfacing with Glazier. Relatively empty.

### `src/testing/`

Contains the TestHarness type, various helper widgets for writing tests, and the snapshot testing code.

### `src/text/`

Contains text-handling code, for both displaying and editing text. Hasn't been maintained in a while, here be dragons.

### `src/widget/`

Contains widget-related items, including the Widget trait, and the WidgetRef, WidgetMut, and WidgetPod types.

Also includes a list of basic widgets, each defined in a single file.

### `src/app_root.rs`

The composition root of the framework. See **General architecture** section.

### `src/debug_logger.rs`, `src/debug_values.rs`

WIP logger to get record of widget passes. See issue #11.

## Module organization principles

(Some of these principles aren't actually applied in the codebase yet. See issue #14 on Github.)

### Module structure

Virtually every module should be private. The only public modules should be inline module blocks that gather public-facing re-exports.

Most items should be exported from the root module, with no other public-facing export. This makes documentation more readable; readers don't need to click on multiple modules to find the item they're looking for.

There should be only three public modules:

- `widgets`
- `commands`
- `test_widgets`

Module files should not be `foobar/mod.rs`. Instead, they should be `_foobar.rs` (thus in the parent folder); the full name is for readability, the leading underscore is so these names appear first in file hierarchies.

### Imports

We should avoid `use super::xxx` imports, except for specific cases like unit tests.

Most imports in the code base should use the canonical top-level import.

### No prelude

Masonry should have no prelude. Examples and documentation should deliberately have a list of imports that cover everything users will need, so users can copy-paste these lists.


## General architecture

### Platform handlers

The composition root of Masonry is **AppRoot** and indirectly **AppRootInner** and **WindowRoot**. They are used by **MasonryWinHandler** and **MasonryAppHandler**. AppRoot, AppRootInner, and WindowRoot are in `src/app_root.rs`, MasonryWinHandler and MasonryAppHandler are in `src/platform/win_handler.rs`.

In more detail:

- **AppRootInner** is the real composition root. There is only a single one for any Masonry program. It calls WindowRoot methods in response to various events.
- **AppRoot** is the publicly exported root. It only owns a`Rc<RefCell<AppRootInner>>` and its methods mostly just do locking and call AppRootInner methods.
- Each window open by Glazier owns a **MasonryWinHandler**, which implements `glazier::WinHandler`. Each MasonryWinHandler holds an AppRoot.
- The global application managed by Glazier owns a **MasonryAppHandler** which implements `glazier::AppHandler`. That MasonryAppHandler holds an AppRoot. These types are almost exclusively used in MacOS apps.
- AppRootInner owns a collection of **WindowRoot**. Each of them stores the Masonry data (widget tree, event data, other metadata) of a single window.

To summarize, an application with N open windows will have:

- N instances of MasonryWinHandler.
- 1 instance of MasonryAppHandler.
- N + 1 instances of AppRoot, which are shared references to...
- 1 instance of AppRootInner, which stores a vec of...
- N instances of WindowRoot.

Additionally, each WindowRoot also stores a **WindowHandle**, which is a lightweight reference to the data structure created by Glazier to represent the window's platform-specific data (eg resource descriptors, rendering surface, etc).

The high-level control flow of a Masonry app's render loop is usually:

- The platform library (windows, x11, gtk, etc) runs some callbacks written in Glazier in response to user interactions, timers, or other events.
- The callbacks call MasonryWinHandler methods.
- Each method calls a single AppRoot method.
- That method calls some AppRootInner method.
- AppRootInner does a bunch of bookkeeping and calls WindowRoot methods.
- WindowRoot does a bunch of bookkeeping and calls the root WidgetPod's on_event/lifecycle/layout/paint methods.

#### WindowConfig vs WindowDescription vs WindowBuilder

WindowConfig, WindowDescription, and WindowBuilder have similar names and similar roles, so it might be a bit hard to tell them apart. A quick primer:

- **WindowConfig** includes a bunch of window-specific metadata. Things like the window size, whether it's maximized, etc. That is used when creating the window, and also to update it.
- **WindowDescription** is WindowConfig plus some Masonry-specific data, such as a WidgetPod of the root widget. It's only used when creating a new window.
- **WindowBuilder** is a type exported by Glazier. When creating a new window, you first instantiate a WindowBuilder and give it config options as well as a type-erased instance of the MasonryWinHandler that will get events for that window.


### Widget hierarchy and passes

The **Widget** trait is defined in `src/widget/widget.rs`. Most of the widget-related bookkeeping is done by the **WidgetPod** type defined in `src/widget/widget_pod.rs`.

A WidgetPod is the main way to store a child for container widgets. In general, the widget hierarchy looks like a tree of container widgets, with each container owning a WidgetPod or a Vec of WidgetPod or something similar. When a pass (on_event/lifecycle/layout/paint) is run on a window, the matching method is called on the root WidgetPod, which recurses to its widget, which calls the same method on each of its children.

Currently, container Widgets are encouraged to call pass methods on each of their children, even when the pass only concerns a single child (eg a click event where only one child is under the mouse); the filtering, if any, is done in WidgetPod.

The current passes are:

- **on_event:** Handles UX-related events, eg user interactions, timers, and IME updates. Widgets can declare these events as "handled" which has a bunch of semantic implications.
- **on_status_change:** TODO.
- **lifecycle:** Handles internal events, eg when the widget is marked as "disabled".
- **layout:** Given size constraints, return the widget's size. Container widgets first call their children's layout method, then set the position and layout date of each child.
- **paint** Paint the widget and its children.

The general pass order is "For each user event, call on_event once, then lifecycle a variable number of times, then schedule a paint. When the platform starts the paint, run layout, then paint".


### WidgetMut

In Masonry, widgets can't be mutated directly. All mutations go through a `WidgetMut` wrapper. So, to change a label's text, you might call `WidgetMut<Label>::set_text()`. This helps Masonry make sure that internal metadata is propagated after every widget change.

Generally speaking, to create a WidgetMut, you need a reference to the parent context that will be updated when the WidgetMut is dropped. That can be the WidgetMut of a parent, an EventCtx / LifecycleCtx, or the WindowRoot. In general, container widgets will have methods such that you can get a WidgetMut of a child from the WidgetMut of a parent.

WidgetMut gives direct mutable access to the widget tree. This can be used by GUI frameworks in their update method, and it can be used in tests to make specific local modifications and test their result.


### Tests

Masonry is designed to make unit tests easy to write, as if the test function were a mouse-and-keyboard user.

Testing is provided by the **TestHarness** type implemented in the `src/testing/harness.rs` file.

Ideally, the harness should provide ways to emulate absolutely every feature that Masonry apps can use. Besides the widget tree, that means keyboard events, mouse events, IME, timers, communication with background threads, animations, accessibility info, etc.

(TODO - Some of that emulation support is not implemented yet. See issue #12.)

Each widget has unit tests in its module and major features have modules with dedicated unit test suites. Ideally, we would like to achieve complete coverage within the crate.

#### Mock timers

For timers in particular, the framework does some special work to simulate the GUI environment.

The GlobalPassCtx types stores two timer handlers: **timers** and **mock_timer_queue**. The first one connects ids returned by the platform's timer creator to widget ids; the second one stores a list of timer values that have to be manually advanced by calling `TestHarness::move_timers_forward`.

When a widget calls `request_timer` in a normal running app, a normal timer is requested from the platform. When a widget calls `request_timer` from a simulated app inside a TestHarness, mock_timer_queue is used instead.

All this means you can have timer-based tests without *actually* having to sleep for the duration of the timer.
30 changes: 30 additions & 0 deletions crates/masonry/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# How to contribute

Issues and PRs are welcome. See [help-wanted issues](https://github.com/PoignardAzur/masonry-rs/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) if you don't know where to begin.

## Issues

Issues don't have a specific format.

If you submit one, please be polite, remember that most Masonry contributors are unpaid volunteers, and include detailed steps to reproduce any problem.


## Pull requests

All submissions, including submissions by project members, require review. We use GitHub Pull Requests for this purpose. Consult GitHub Help for more information on using pull requests.

Before making a PR, please follow these steps:

- Run `cargo test --all-targets` on your code.
- Run `cargo test --doc`.
- Run `cargo clippy --all-targets`.

Masonry doesn't keep a changelog for now, but might in the future. Make sure your commit messages and pull request titles explain what you have changed.
How to maintain


## Preparing for a new release

Before a new release, make sure to update every dependency to their last version.

You can use `cargo upgrade` from the `cargo-edit` tool to help with the process.
Loading

0 comments on commit 9b2ebfc

Please sign in to comment.