Work on guide

This commit is contained in:
Jonathan Johnson 2024-01-06 10:26:23 -08:00
parent bb28f96b58
commit 8fb372e743
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
17 changed files with 366 additions and 133 deletions

View file

@ -5,7 +5,6 @@ on: [push]
jobs:
docs:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
@ -32,4 +31,34 @@ jobs:
api-key: ${{ secrets.DOSSIER_API_KEY }}
project: cushy
from: target/doc/
to: /${{ github.ref_name }}/docs
to: /${{ github.ref_name }}/docs
guide:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Download mdbook
run: |
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.36/mdbook-v0.4.36-x86_64-unknown-linux-gnu.tar.gz | tar -xz
- name: Install mdbook-variables
run: |
cargo install mdbook-variables
- name: Build Guide
run: |
./mdbook build guide
- name: Deploy
uses: khonsulabs/sync-to-dossier@main
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/')
with:
url: ${{ secrets.DOSSIER_URL }}
api-key-id: ${{ secrets.DOSSIER_API_KEY_ID }}
api-key: ${{ secrets.DOSSIER_API_KEY }}
project: cushy
from: target/guide/
to: /${{ github.ref_name }}/guide

View file

@ -4,3 +4,10 @@ language = "en"
multilingual = false
src = "src"
title = "Cushy User's Guide"
[build]
build-dir = "../target/guide"
extra-watch-dirs = ["./guide-examples/"]
[preprocessor.variables.variables]
docs = "https://cushy.rs/main/docs/cushy"

View file

@ -1,133 +1,167 @@
use std::array;
use cushy::figures::units::{Lp, Px};
use cushy::figures::{Point, Size};
use cushy::styles::{Edges, ThemePair};
use cushy::styles::ThemePair;
use cushy::widget::MakeWidget;
use cushy::widgets::Space;
use guide_examples::BookExample;
use cushy::widgets::grid::{GridDimension, GridWidgets};
use cushy::widgets::{Grid, Space};
use guide_examples::book_example;
// ANCHOR: content
fn content() -> impl MakeWidget {
Space::primary().size(Size::squared(Px::new(32)))
Space::primary().size(Size::squared(Px::new(32)..))
}
// ANCHOR_END: content
fn align_left() -> impl MakeWidget {
// ANCHOR: align-left
content().align_left()
// ANCHOR_END: align-left
}
fn centered() -> impl MakeWidget {
// ANCHOR: horizontal-center
content().centered()
// ANCHOR_END: horizontal-center
}
fn align_right() -> impl MakeWidget {
// ANCHOR: align-right
content().align_right()
// ANCHOR_END: align-right
}
fn align_horizontal() -> impl MakeWidget {
Grid::from_rows(
GridWidgets::new()
.and(("Unaligned", content()))
.and(("align_left()", align_left()))
.and(("centered()", centered()))
.and(("align_right()", align_right())),
)
.dimensions([
GridDimension::FitContent,
GridDimension::Fractional { weight: 1 },
])
}
fn align_top() -> impl MakeWidget {
// ANCHOR: align-top
content().align_top()
// ANCHOR_END: align-top
}
fn align_bottom() -> impl MakeWidget {
// ANCHOR: align-bottom
content().align_bottom()
// ANCHOR_END: align-bottom
}
fn align_vertical() -> impl MakeWidget {
Grid::from_rows(
GridWidgets::new()
.and(("Unaligned", "align_top()", "centered()", "align_bottom()"))
.and((
content().height(Lp::inches(1)).centered(),
align_top(),
centered(),
align_bottom(),
)),
)
.dimensions(array::from_fn(|_| GridDimension::Fractional { weight: 1 }))
}
fn align() -> impl MakeWidget {
"Horizontal Alignment"
.and(align_horizontal().contain())
.and("Vertical Alignment")
.and(align_vertical().contain())
.into_rows()
}
fn main() {
BookExample::new(
"align-horizontal",
"Default Behavior"
.and(content())
.and("align_left()")
.and({
// ANCHOR: align-left
content().align_left()
// ANCHOR_END: align-left
})
.and("pad_by().align_left()")
.and({
// ANCHOR: align-left-pad
content()
.pad_by(Edges::default().with_left(Lp::inches(1)))
.align_left()
// ANCHOR_END: align-left-pad
})
.and("centered()")
.and({
// ANCHOR: centered
content().centered()
// ANCHOR_END: centered
})
.and("pad_by().align_right()")
.and({
// ANCHOR: align-right-pad
content()
.pad_by(Edges::default().with_right(Lp::inches(1)))
.align_right()
// ANCHOR_END: align-right-pad
})
.and("align_right()")
.and({
// ANCHOR: align-right
content().align_right()
// ANCHOR_END: align-right
})
.into_rows(),
)
.still_frame(|recorder| {
const LEFT: u32 = 40;
const PADDING: u32 = 96;
const RIGHT: u32 = 710;
const CENTER: u32 = 375;
let theme = ThemePair::default();
let container_color = theme.dark.surface.low_container;
let primary = theme.dark.primary.color;
book_example!(align).still_frame(|recorder| {
const LEFT: u32 = 145;
const RIGHT: u32 = 705;
const H_CENTER: u32 = (RIGHT + LEFT) / 2;
const TOP: u32 = 282;
const BOTTOM: u32 = 345;
const V_CENTER: u32 = (TOP + BOTTOM) / 2;
let container_color = ThemePair::default().dark.surface.lowest_container;
let primary = ThemePair::default().dark.primary.color;
recorder.assert_pixel_color(Point::new(LEFT, 35), container_color, "surface");
// Verify the inner container color
recorder.assert_pixel_color(Point::new(32, 62), container_color, "surface");
// Default fills the entire space
recorder.assert_pixel_color(Point::new(LEFT, 70), primary, "default spacer");
recorder.assert_pixel_color(Point::new(CENTER, 70), primary, "default spacer");
recorder.assert_pixel_color(Point::new(RIGHT, 70), primary, "default spacer");
recorder.assert_pixel_color(Point::new(LEFT, 78), primary, "default spacer");
recorder.assert_pixel_color(Point::new(H_CENTER, 78), primary, "default spacer");
recorder.assert_pixel_color(Point::new(RIGHT, 78), primary, "default spacer");
// align-left
recorder.assert_pixel_color(Point::new(LEFT, 140), primary, "align-left spacer");
recorder.assert_pixel_color(Point::new(LEFT, 110), primary, "align-left spacer");
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 140),
Point::new(H_CENTER, 110),
container_color,
"align-left empty",
);
// align-left-pad
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 215),
primary,
"align-left-pad spacer",
);
recorder.assert_pixel_color(
Point::new(LEFT, 215),
container_color,
"align-left-pad empty before",
);
recorder.assert_pixel_color(
Point::new(CENTER, 215),
container_color,
"align-left-pad empty after",
);
// centered
recorder.assert_pixel_color(Point::new(CENTER, 295), primary, "centered spacer");
recorder.assert_pixel_color(Point::new(H_CENTER, 142), primary, "centered spacer");
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 295),
Point::new(LEFT, 142),
container_color,
"centered empty before",
);
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 295),
Point::new(RIGHT, 142),
container_color,
"centered empty after",
);
// align-right-pad
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 360),
primary,
"align-right-pad spacer",
);
recorder.assert_pixel_color(
Point::new(CENTER, 360),
container_color,
"align-right-pad empty before",
);
recorder.assert_pixel_color(
Point::new(RIGHT, 360),
container_color,
"align-right-pad empty after",
);
// align-right
recorder.assert_pixel_color(Point::new(RIGHT, 435), primary, "align-right spacer");
recorder.assert_pixel_color(Point::new(RIGHT, 175), primary, "align-right spacer");
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 435),
Point::new(V_CENTER, 175),
container_color,
"align-right empty",
);
// Default fills the entire space
recorder.assert_pixel_color(Point::new(115, TOP), primary, "default spacer");
recorder.assert_pixel_color(Point::new(115, V_CENTER), primary, "default spacer");
recorder.assert_pixel_color(Point::new(115, BOTTOM), primary, "default spacer");
// align-top
recorder.assert_pixel_color(Point::new(285, TOP), primary, "align-top spacer");
recorder.assert_pixel_color(
Point::new(285, V_CENTER),
container_color,
"align-top empty",
);
// centered
recorder.assert_pixel_color(Point::new(460, V_CENTER), primary, "centered spacer");
recorder.assert_pixel_color(
Point::new(460, TOP),
container_color,
"centered empty before",
);
recorder.assert_pixel_color(
Point::new(460, BOTTOM),
container_color,
"centered empty after",
);
// align-bottom
recorder.assert_pixel_color(Point::new(635, BOTTOM), primary, "align-bottom spacer");
recorder.assert_pixel_color(
Point::new(635, V_CENTER),
container_color,
"align-bottom empty",
);
});
}

View file

@ -67,3 +67,10 @@ impl BookExample {
// {
// }
}
#[macro_export]
macro_rules! book_example {
($name:ident) => {
guide_examples::BookExample::new(stringify!($name), $name())
};
}

View file

@ -1,3 +1,42 @@
# Summary
- [Chapter 1](./chapter_1.md)
<!-- markdownlint-disable no-empty-links -->
[Introduction](./intro.md)
- [About Cushy](./about.md)
- [Cushy's Philosophies](./about/philosophies.md)
- [Widgets]()
- [Layout Widgets](./widgets/layout.md)
- [Align](./widgets/layout/align.md)
- [Collapse]()
- [Container]()
- [Expand]()
- [Grid]()
- [Layers]()
- [Resize]()
- [Stack]()
- [Wrap]()
- [Controls](./widgets/controls.md)
- [Button]()
- [Canvas]()
- [Checkbox]()
- [Color Pickers]()
- [Disclose]()
- [Input]()
- [Label]()
- [ProgressBar]()
- [Radio]()
- [Scroll]()
- [Select]()
- [Slider]()
- [Switcher]()
- [Image]()
- [TileMap]()
- [Utility Widgets](./widgets/utility.md)
- [Custom]()
- [Data]()
- [Style]()
- [Themed]()
- [ThemedMode]()
- [Space](./widgets/utility/space.md)

1
guide/src/about.md Normal file
View file

@ -0,0 +1 @@
# About

View file

@ -0,0 +1,34 @@
# Cushy's Philosophies
There are a lot of GUI libraries with wildly varying approaches to how UIs are
displayed. Here's the philosophies that drive Cushy's design:
- Cushy retains information between redraws so that many events can be handled
without redrawing the user interface.
- Everything is a widget. The "root" of a user interface/window is a widget, and
widgets can contain other widgets.
- Composition is powerful and easy to reason about. The built-in widget library
is aimed at providing a suite of libraries each with an individual purpose to
aide in developers being able to compose more complex user interfaces from
more basic widgets.
- If a developer dislikes a built-in widget's behavior, they should be empowered
to create their own that behaves the way they desire. To ensure developers
have this flexibility, all provided widgets must only utilize functionality
that is publicly available.
- Widgets should be flexible in the types they support, prefering trait
implementations instead of hard-coded types. For example, the Label widget
supports any type that implements `Display`.
- Cushy needs both physical pixel and resolution independent measurement types.
UI designers want to use real-world measurements that scale based on the DPI
resolution of the device it is being rendered on. Widget authors and game
developers want to work with pixel-perfect measurements to ensure perfect
alignment.
From an implementation standpoint, Cushy has these goals:
- For graphics, provide a wgpu-centric library that exposes a rendering API
inspired by wgpu's Encapsulating Graphics Work article.
- For windowing, embrace winit and route input events to the correct widgets.
This allows widgets to support any features that winit can support.
- Cushy should be able to idle at close to 0% CPU. Cushy should not redraw
unless needed.

View file

@ -1,33 +0,0 @@
# Aligning Widgets
![align.rs - horizontal-align](/examples/align-horizontal.png)
## Align a widget to the left
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-left}}
```
## Align a widget to the left, with padding
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-left-pad}}
```
## Align a widget to the center
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:centered}}
```
## Align a widget to the right, with padding
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-right-pad}}
```
## Align a widget to the right
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-right}}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

1
guide/src/intro.md Normal file
View file

@ -0,0 +1 @@
# Welcome

View file

@ -0,0 +1,4 @@
# Controls
This section contains interactive widgets that read and/or write data through
the reactive data model.

View file

@ -0,0 +1,4 @@
# Layout
Widgets in this section have a primary purpose of performing some layout
functionality on one or more child widgets.

View file

@ -0,0 +1,96 @@
# Aligning Widgets
{{#title Align - Layout Widgets - Cushy User's Guide}}
The [`Align`][align] widget positions a child widget within its parent. It
supports both horizontal and vertical alignment.
It accomplishes this by requesting the child measure itself using
[`SizeToFit`][sizetofit] for the child's width and/or height, and then positions
the child to align it.
The align widget uses [`Edges`][edges][`<FlexibleDimension>`][flexibledimension]
to specify the alignment of each edge. If an edge is
[`FlexibleDimension::Dimension`][flexexact], that edge of the child will be
placed the exact measurement from the parent's matching edge. If an edge is
[`FlexibleDimension::Auto`][flexauto], that edge will not be positioned relative
to the parent's matching edge.
## Examples
![Align widget example](/examples/align.png)
The `content()` function in each of these snippets is a [`Space`][space] widget
occupying at least 32px squared:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:content}}
```
### Align a widget to the left
Any widget can be aligned to the left using
[`MakeWidget::align_left()`][align-left]:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:align-left}}
```
### Align a widget to the center
Any widget can be centered using
[`MakeWidget::centered()`][align-center]:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:horizontal-center}}
```
`centered()` works in both axis. To center only in one direction, "fit" the
other direction:
- To center vertically but occupy the parent's width, use
[`MakeWidget::fit_horizontally()`][fit-vert].
- To center horizontally but occupy the parent's height, use
[`MakeWidget::fit_vertically()`][fit-horiz].
### Align a widget to the right
Any widget can be aligned to the right using
[`MakeWidget::align_right()`][align-right]:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:align-right}}
```
### Align a widget to the top
Any widget can be aligned to the top using
[`MakeWidget::align_top()`][align-top]:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:align-top}}
```
### Align a widget to the bottom
Any widget can be aligned to the bottom using
[`MakeWidget::align_bottom()`][align-bottom]:
```rust,no_run,no_playground
{{#include ../../../guide-examples/examples/align.rs:align-bottom}}
```
[align]: <{{ docs }}/widgets/struct.Align.html>
[align-left]: <{{ docs }}/widget/trait.MakeWidget.html#method.align_left>
[align-center]: <{{ docs }}/widget/trait.MakeWidget.html#method.centered>
[align-right]: <{{ docs }}/widget/trait.MakeWidget.html#method.align_right>
[align-top]: <{{ docs }}/widget/trait.MakeWidget.html#method.align_top>
[align-bottom]: <{{ docs }}/widget/trait.MakeWidget.html#method.align_bottom>
[sizetofit]: <{{ docs }}/enum.ConstraintLimit.html#variant.SizeToFit>
[edges]: <{{ docs }}/styles/struct.Edges.html>
[flexibledimension]: <{{ docs }}/styles/enum.FlexibleDimension.html>
[flexexact]: <{{ docs }}/styles/enum.FlexibleDimension.html#variant.Dimension>
[flexauto]: <{{ docs }}/styles/enum.FlexibleDimension.html#variant.Auto>
[fit-vert]: <https://cushy.rs/main/docs/cushy/widget/trait.MakeWidget.html#method.fit_vertically>
[fit-horiz]: <https://cushy.rs/main/docs/cushy/widget/trait.MakeWidget.html#method.fit_horizontally>
[space]: ../utility/space.md

View file

@ -0,0 +1 @@
# Layout Widgets

View file

@ -0,0 +1,4 @@
# Utility Widgets
The widgets in this section are non-interactive widgets that do not directly
impact the layout of other widgets.

View file

@ -0,0 +1,5 @@
# Space Widget
The [`Space`][space]
[space]: <{{ docs }}/widgets/struct.Space.html>