Documentation
Added documentation to many public functions Added Codex documentaiton for the project as a whole Added build pipline for generating codex site
This commit is contained in:
parent
6abfc6aa54
commit
c3a14d6c33
|
@ -0,0 +1,40 @@
|
||||||
|
name: Publish Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/5Sigma/codex/releases/latest/download/Codex-installer.sh | sh
|
||||||
|
codex -r docs build
|
||||||
|
- name: Upload Pages artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: docs/dist
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Project name
|
||||||
|
name: Arkham CLI Library
|
||||||
|
# The author displayed on the title page during PDF generation
|
||||||
|
author: ~
|
||||||
|
# The path relative to the project root where the compiled static site files
|
||||||
|
# will be placed
|
||||||
|
build_path: dist
|
||||||
|
# The URL to the code repository for the project. If specified a link will appear
|
||||||
|
# in the site header.
|
||||||
|
repo_url: https://github.com/5sigma/arkham
|
||||||
|
# The URL to the main project page. If specified a home link will be displayed
|
||||||
|
# in the header.
|
||||||
|
project_url: ~
|
||||||
|
# Base URL can be set if the site is hosted in a sub path.
|
||||||
|
base_url: /arkham
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Document Title
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: Designing Components
|
||||||
|
subtitle: Overview
|
||||||
|
menu_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
# Component overview
|
||||||
|
|
||||||
|
A component in Arkham is a piece of rendering logic that could be reusable, or could be used for organization.
|
||||||
|
They are simple functions that accept a `ViewContext` reference, alongside any injected objects. See [Dependency Injection](resources)
|
||||||
|
|
||||||
|
A simple component might look like this:
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn seperator(ctx: &mut ViewContext) {
|
||||||
|
let width = ctx.size().width;
|
||||||
|
let sep_chars = "-".repeat(ctx.size().width);
|
||||||
|
ctx.insert(sep_chars);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components with parameters
|
||||||
|
|
||||||
|
Reusable components will need the ability to pass parameters into them. This is done by returning the component function from within another function.
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn name_field(name: &str) -> impl Fn(&mut ViewContext) {
|
||||||
|
move |ctx:&mut ViewContext| {
|
||||||
|
ctx.insert((0,0), "Name:");
|
||||||
|
ctx.insert((10,0), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Understanding component sizing and positioning
|
||||||
|
|
||||||
|
When a component is used it is allocated a specific `Rect` that it can render to.
|
||||||
|
The total dimensions for the component are available in `ViewContext::size()`.
|
||||||
|
Components are also rendered at a specific position (the upper left corner).
|
||||||
|
Inside a component its coordinates are relative to itself, its upper left corner is (0,0).
|
||||||
|
|
||||||
|
|
||||||
|
## Using components
|
||||||
|
|
||||||
|
A component can render other components inside them.
|
||||||
|
|
||||||
|
In this example we can also see the component positioning and sizing.
|
||||||
|
The first parameter to `ViewContext::component` is a Rect for the component.
|
||||||
|
This is the position in the parent component it will render its top left corner
|
||||||
|
to and the size the component is allowed to render to.
|
||||||
|
|
||||||
|
The first parameter to `Rect::new` is the `Pos` for the rect, the coordinates
|
||||||
|
of its top left corner. From the perspective of the `field` component,
|
||||||
|
the content is inserted at y=0. However, when the component is placed in
|
||||||
|
the `container` component, its upper left coordinate is specified and the
|
||||||
|
coordinates of the `field` component become relative to the specified position
|
||||||
|
in the `container` component.
|
||||||
|
|
||||||
|
The second parameter to `Rect::new` is the `Size` of the `Rect`. Its width and height.
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn field(key: &str, value: &str) -> impl Fn(&mut ViewContext) {
|
||||||
|
move |ctx: &mut ViewContext| {
|
||||||
|
ctx.insert((0,0), format!("{}:", key));
|
||||||
|
ctx.insert((10,0), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn container(ctx: &mut ViewContext) {
|
||||||
|
ctx.component(Rect::new((0,0), (10,1)), field("Name", "Alice")
|
||||||
|
ctx.component(Rect::new((0,1), (10,1)), field("Age", "22")
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The `container` component will render to the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
Name: Alice
|
||||||
|
Age: 22
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Components as closures
|
||||||
|
|
||||||
|
When you need something more complex than `ViewContext::insert`, but don't want to
|
||||||
|
build a whole separate component. Components can be defined inline using a closure.
|
||||||
|
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn container(ctx: &mut ViewContext) {
|
||||||
|
let size = ctx.size();
|
||||||
|
ctx.component(
|
||||||
|
Rect::new((0,0), (size.width, size.height /2))
|
||||||
|
|ctx: &mut ViewContext| {
|
||||||
|
ctx.fill_all(Color::Blue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
subtitle: Overview
|
||||||
|
menu_position: 0
|
||||||
|
---
|
||||||
|
|
||||||
|
# Starting a new project
|
||||||
|
|
||||||
|
To setup a new project use `cargo new` and `cargo add arkham` to add the dependency.
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
cargo new my_project
|
||||||
|
cd my_project
|
||||||
|
cago add arkham
|
||||||
|
```
|
||||||
|
|
||||||
|
# Import the arkham prelude
|
||||||
|
|
||||||
|
Add the following line to the top of _my_project/src/main.rs_ to import all the Arkham members.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use arkham::prelude::*;
|
||||||
|
```
|
||||||
|
|
||||||
|
# Setup the root view
|
||||||
|
|
||||||
|
Arkham requires a _Root View_ component that will act as a container view for the application. Views in Arkham are simple functions.
|
||||||
|
We can add a root view function to _my_project/src/main.rs_
|
||||||
|
A simple root view may look like this:
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn root_view(ctx &mut ViewContext) {
|
||||||
|
ctx.insert((5,5), "Hello World");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Setup the application
|
||||||
|
|
||||||
|
In our `main` function we can setup the application and run it, beginning the run loop. Replace the main function in _my_project/src/main.rs_ with the following:
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn main() {
|
||||||
|
App::new(root_view).run().unwrap();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# The full main.rs
|
||||||
|
|
||||||
|
The full main.rs now looks like this:
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
use arkham::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new(root_view).run().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_view(ctx &mut ViewContext) {
|
||||||
|
ctx.insert((5,5), "Hello World");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
This can now be run with `cargo run` and a hello world app, complete with a default 'q' hotkey to quit, will run.
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
title: Keyboard Input
|
||||||
|
subtitle: Overview
|
||||||
|
menu_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reading keyboard input
|
||||||
|
|
||||||
|
Keyboard input is provided by a `Keyboard` resource. This is
|
||||||
|
automatically available. To read input from keyboard events
|
||||||
|
accept the resource as a parameter for a component function
|
||||||
|
and check the current state.
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn show_keypress(ctx &mut ViewContext, kb: Res<Keyboard>) {
|
||||||
|
if let Some(c) = kb.char() {
|
||||||
|
ctx.insert(0, format!("Key press: {}", c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reading modifier keys
|
||||||
|
|
||||||
|
Modifier key state is provided within the keyboard resource.
|
||||||
|
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn check_key(ctx &mut ViewContext, kb: Res<Keyboard>) {
|
||||||
|
if kb.char() == Some('d') && kb.control() {
|
||||||
|
ctx.insert(0, "Key Pressed")
|
||||||
|
} else {
|
||||||
|
ctx.insert(0, "Key NOT Pressed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Full Keyboard example
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
use arkham::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new(root).run().expect("couldnt launch app");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root(ctx: &mut ViewContext) {
|
||||||
|
let size = ctx.size();
|
||||||
|
ctx.fill(size, Rune::new().bg(Color::DarkGrey));
|
||||||
|
ctx.component(((10, 10), (30, 1)), hello_world);
|
||||||
|
ctx.component(((10, 11), (20, 1)), show_key_press);
|
||||||
|
ctx.component((0, (size.width, 1)), quit_nag);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hello_world(ctx: &mut ViewContext) {
|
||||||
|
ctx.insert(0, "Hello World, Press a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_key_press(ctx: &mut ViewContext, kb: Res<Keyboard>) {
|
||||||
|
if let Some(c) = kb.char() {
|
||||||
|
ctx.insert(0, format!("Key press: {}", c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit_nag(ctx: &mut ViewContext) {
|
||||||
|
let size = ctx.size();
|
||||||
|
ctx.insert(
|
||||||
|
((size.width / 2) - 7, 0),
|
||||||
|
"Press Q to Quit".to_runes().fg(Color::Red),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,98 @@
|
||||||
|
---
|
||||||
|
title: Resources & State
|
||||||
|
subtitle: Overview
|
||||||
|
menu_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dependency Injection
|
||||||
|
|
||||||
|
Being able to access your own objects and state is
|
||||||
|
important for any application. Arkham focuses heavily
|
||||||
|
on making this ergonomic and easy.
|
||||||
|
|
||||||
|
There are two types of injectable objects a _State_
|
||||||
|
object and _Resource_ object. The main difference is
|
||||||
|
Resource objects are provided immutable and State
|
||||||
|
objects are provided with the ability to borrow both
|
||||||
|
as mutable and immutable references.
|
||||||
|
|
||||||
|
## Defining an injectable object
|
||||||
|
|
||||||
|
_Resources_ and _state_ are added during application startup
|
||||||
|
and are global to the application. A single state object can
|
||||||
|
be used to maintain the full state of the application and
|
||||||
|
individual components can read and write to the sections they
|
||||||
|
need.
|
||||||
|
|
||||||
|
Resources and state must have unique _Type_. Only one instance
|
||||||
|
any type can be inserted.
|
||||||
|
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
|
||||||
|
pub struct Person {
|
||||||
|
pub name: String,
|
||||||
|
pub age: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub counter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
let people: Vec<Person> = load_people();
|
||||||
|
|
||||||
|
App::new(root_view)
|
||||||
|
.insert_resource(people)
|
||||||
|
.insert_state(AppState::default())
|
||||||
|
.run();
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Using resources and state
|
||||||
|
|
||||||
|
Injectables are provided automatically to any component that accepts them.
|
||||||
|
Accepting them requires the use of a wrapper component depending on which
|
||||||
|
it is.
|
||||||
|
|
||||||
|
- Resources use `Res<T>`
|
||||||
|
- State objects use `State<T>`
|
||||||
|
|
||||||
|
## Using a resource
|
||||||
|
|
||||||
|
Including the resource in the function arguments automatically provides
|
||||||
|
the object inside a `Res` wrapper, which derefs to the underlying object.
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn my_component(ctx: &mut ViewContext, people: Res<People>) {
|
||||||
|
for (idx, person) in people.iter().enumerate() {
|
||||||
|
ctx.insert((0, idx), person.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using state
|
||||||
|
|
||||||
|
|
||||||
|
Including the resource in the function arguments automatically provides
|
||||||
|
the object inside a `State` wrapper. The state wrapper has two primary
|
||||||
|
functions `State::get` which returns immutable access to the state
|
||||||
|
and `State::get_mut` which returns a mutable reference.
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
fn my_component(ctx: &mut ViewContext, state: State<AppState>) {
|
||||||
|
ctx.insert(
|
||||||
|
(0, 0),
|
||||||
|
format!("Counter: {}", state.get().counter));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<Alert style="warning" title="Don't borrow state mutably more than once">
|
||||||
|
Under the hood state is provided inside `Rc<RefCell<T>>`.
|
||||||
|
Take care not to call `State::get_mut`, which is effectively calling `RefCell::borrow_mut` more than
|
||||||
|
once at a time.
|
||||||
|
|
||||||
|
This includes holding it and then calling a sub component that attempts to access state again.
|
||||||
|
Scope calls to `State::get_mut` so they live as short as possible and clone out objects if needed.
|
||||||
|
</Alert>
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
---
|
||||||
|
title: Stacks
|
||||||
|
subtitle: Overview
|
||||||
|
menu_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
# Stack Components
|
||||||
|
|
||||||
|
Stacks are a convenient layout component available through the `ViewContext`.
|
||||||
|
They allow a number of components to be easily positioned next to each other.
|
||||||
|
|
||||||
|
Stacks can be either _horizontal_ or _vertical_.
|
||||||
|
|
||||||
|
To use a stack initialize it from the context and use the `Stack::insert`
|
||||||
|
and `Stack:component` functions. The `Stack::component` function requires only a
|
||||||
|
`Size` and not a `Rect` like the `ViewContext`. This is because the stack will
|
||||||
|
automatically handle its positioning.
|
||||||
|
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
let size = ctx.size();
|
||||||
|
let mut stack = ctx.vertical_stack(Size::new(100, 100));
|
||||||
|
stack.component((size.width, 2), my_component);
|
||||||
|
// We can pass size here and it will be automtically
|
||||||
|
// converted to a Rect with position (0,0).
|
||||||
|
ctx.component(size, stack);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alignment
|
||||||
|
|
||||||
|
Stacks can also have a specified alignment. This will modify the
|
||||||
|
positioning of the sub components so they align in the given direction.
|
||||||
|
|
||||||
|
Vertical stacks can have Left, Center, Right alignments
|
||||||
|
Horizontal stacks can have Top, Center, Bottom alignments
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
let size = ctx.size();
|
||||||
|
let mut stack = ctx.vertical_stack(Size::new(100, 100));
|
||||||
|
stack.alignment(StackAlignment::Center);
|
||||||
|
```
|
||||||
|
|
||||||
|
# Full stack example
|
||||||
|
|
||||||
|
```Rust
|
||||||
|
use arkham::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = App::new(root).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root(ctx: &mut ViewContext) {
|
||||||
|
let mut stack = ctx.vertical_stack((100, 100));
|
||||||
|
for _ in 0..10 {
|
||||||
|
stack.component((ctx.size().width, 1), list_item);
|
||||||
|
}
|
||||||
|
ctx.component((0, (100, 100)), stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_item(ctx: &mut ViewContext) {
|
||||||
|
let size = ctx.size();
|
||||||
|
let mut hstack = ctx.horizontal_stack((ctx.size().width, 1));
|
||||||
|
hstack.insert("> ");
|
||||||
|
hstack.insert("line 1");
|
||||||
|
ctx.component(size, hstack);
|
||||||
|
}
|
||||||
|
```
|
|
@ -7,9 +7,9 @@ fn main() {
|
||||||
fn root(ctx: &mut ViewContext) {
|
fn root(ctx: &mut ViewContext) {
|
||||||
let size = ctx.size();
|
let size = ctx.size();
|
||||||
ctx.fill(size, Rune::new().bg(Color::DarkGrey));
|
ctx.fill(size, Rune::new().bg(Color::DarkGrey));
|
||||||
ctx.component(Rect::new((10, 10), (30, 1)), hello_world);
|
ctx.component(((10, 10), (30, 1)), hello_world);
|
||||||
ctx.component(Rect::new((10, 11), (20, 1)), show_key_press);
|
ctx.component(((10, 11), (20, 1)), show_key_press);
|
||||||
ctx.component(Rect::new(0, (size.width, 1)), quit_nag);
|
ctx.component((0, (size.width, 1)), quit_nag);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hello_world(ctx: &mut ViewContext) {
|
fn hello_world(ctx: &mut ViewContext) {
|
||||||
|
|
|
@ -5,13 +5,17 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root(ctx: &mut ViewContext) {
|
fn root(ctx: &mut ViewContext) {
|
||||||
let mut stack = ctx.vertical_stack(Size::new(100, 100));
|
let mut stack = ctx.vertical_stack((100, 100));
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
stack.component(Size::new(ctx.size().width, 2), list_item);
|
stack.component((ctx.size().width, 1), list_item);
|
||||||
}
|
}
|
||||||
ctx.component(Rect::new(0, (100, 100)), stack);
|
ctx.component((0, (100, 100)), stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_item(ctx: &mut ViewContext) {
|
fn list_item(ctx: &mut ViewContext) {
|
||||||
ctx.insert(0, "line 1");
|
let size = ctx.size();
|
||||||
|
let mut hstack = ctx.horizontal_stack((ctx.size().width, 1));
|
||||||
|
hstack.insert("> ");
|
||||||
|
hstack.insert("line 1");
|
||||||
|
ctx.component(size, hstack);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl Renderer {
|
||||||
///
|
///
|
||||||
/// Setting up a basic application:
|
/// Setting up a basic application:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```no_run
|
||||||
/// use arkham::prelude::*;
|
/// use arkham::prelude::*;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
|
|
|
@ -55,21 +55,27 @@ impl ViewContext {
|
||||||
self.rerender = true;
|
self.rerender = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vertical_stack(&self, size: Size) -> Stack {
|
pub fn vertical_stack<S>(&self, size: S) -> Stack
|
||||||
|
where
|
||||||
|
S: Into<Size>,
|
||||||
|
{
|
||||||
Stack {
|
Stack {
|
||||||
direction: crate::stack::StackDirection::Vertical,
|
direction: crate::stack::StackDirection::Vertical,
|
||||||
container: self.container.clone(),
|
container: self.container.clone(),
|
||||||
view: View::new(size),
|
view: View::new(size.into()),
|
||||||
position: Pos::from(0),
|
position: Pos::from(0),
|
||||||
alignment: crate::stack::StackAlignment::Top,
|
alignment: crate::stack::StackAlignment::Top,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn horizontal_stack(&self, size: Size) -> Stack {
|
pub fn horizontal_stack<S>(&self, size: S) -> Stack
|
||||||
|
where
|
||||||
|
S: Into<Size>,
|
||||||
|
{
|
||||||
Stack {
|
Stack {
|
||||||
direction: crate::stack::StackDirection::Horizontal,
|
direction: crate::stack::StackDirection::Horizontal,
|
||||||
container: self.container.clone(),
|
container: self.container.clone(),
|
||||||
view: View::new(size),
|
view: View::new(size.into()),
|
||||||
position: Pos::from(0),
|
position: Pos::from(0),
|
||||||
alignment: crate::stack::StackAlignment::Left,
|
alignment: crate::stack::StackAlignment::Left,
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,3 +243,13 @@ impl From<Size> for Rect {
|
||||||
Rect::with_size(value)
|
Rect::with_size(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P, S> From<(P, S)> for Rect
|
||||||
|
where
|
||||||
|
P: Into<Pos>,
|
||||||
|
S: Into<Size>,
|
||||||
|
{
|
||||||
|
fn from(value: (P, S)) -> Self {
|
||||||
|
Rect::new(value.0.into(), value.1.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
21
src/input.rs
21
src/input.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyModifiers, ModifierKeyCode};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
|
|
||||||
/// Keyboard can be used as an injectable resource that provides information
|
/// Keyboard can be used as an injectable resource that provides information
|
||||||
/// about the current keyboard state. This is the primary mechanism by which
|
/// about the current keyboard state. This is the primary mechanism by which
|
||||||
|
@ -24,22 +24,31 @@ impl Keyboard {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_key(&self, k: KeyCode) {
|
/// Set the keyboard state to indicate a specific keycode is pressed
|
||||||
|
pub(crate) fn set_key(&self, k: KeyCode) {
|
||||||
*self.key.borrow_mut() = Some(k);
|
*self.key.borrow_mut() = Some(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_modifiers(&self, modifiers: KeyModifiers) {
|
/// Set the keyboard state to indicate specific modifier keys are pressed
|
||||||
|
pub(crate) fn set_modifiers(&self, modifiers: KeyModifiers) {
|
||||||
*self.modifiers.borrow_mut() = modifiers;
|
*self.modifiers.borrow_mut() = modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resets the keyboard state. This can be used after accepting
|
||||||
|
/// a keypress within a component to prevent further components from
|
||||||
|
/// registering the keypress event
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
*self.key.borrow_mut() = None;
|
*self.key.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retruns the keycode that is current pressed, or None if there are
|
||||||
|
/// no currently pressed keys
|
||||||
pub fn code(&self) -> Option<KeyCode> {
|
pub fn code(&self) -> Option<KeyCode> {
|
||||||
*self.key.borrow()
|
*self.key.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the char value of the pressed key. Returns None if no key
|
||||||
|
/// is currently pressed, or if the key does not have a char value.
|
||||||
pub fn char(&self) -> Option<char> {
|
pub fn char(&self) -> Option<char> {
|
||||||
if let Some(KeyCode::Char(c)) = *self.key.borrow() {
|
if let Some(KeyCode::Char(c)) = *self.key.borrow() {
|
||||||
Some(c)
|
Some(c)
|
||||||
|
@ -48,26 +57,32 @@ impl Keyboard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the shift key is current pressed
|
||||||
pub fn shift(&self) -> bool {
|
pub fn shift(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::SHIFT)
|
self.modifiers.borrow().contains(KeyModifiers::SHIFT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the control key is current pressed
|
||||||
pub fn control(&self) -> bool {
|
pub fn control(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::CONTROL)
|
self.modifiers.borrow().contains(KeyModifiers::CONTROL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the alt key is current pressed
|
||||||
pub fn alt(&self) -> bool {
|
pub fn alt(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::ALT)
|
self.modifiers.borrow().contains(KeyModifiers::ALT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the super key is current pressed
|
||||||
pub fn super_key(&self) -> bool {
|
pub fn super_key(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::SUPER)
|
self.modifiers.borrow().contains(KeyModifiers::SUPER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the hyper key is current pressed
|
||||||
pub fn hyper(&self) -> bool {
|
pub fn hyper(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::HYPER)
|
self.modifiers.borrow().contains(KeyModifiers::HYPER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the meta key is current pressed
|
||||||
pub fn meta(&self) -> bool {
|
pub fn meta(&self) -> bool {
|
||||||
self.modifiers.borrow().contains(KeyModifiers::META)
|
self.modifiers.borrow().contains(KeyModifiers::META)
|
||||||
}
|
}
|
||||||
|
|
18
src/stack.rs
18
src/stack.rs
|
@ -178,7 +178,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vertical_insert() {
|
fn test_vertical_insert() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.vertical_stack((10, 2).into());
|
let mut stack = ctx.vertical_stack((10, 2));
|
||||||
stack.insert("one");
|
stack.insert("one");
|
||||||
stack.insert("two");
|
stack.insert("two");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -190,7 +190,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontal_insert() {
|
fn test_horizontal_insert() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.horizontal_stack((10, 1).into());
|
let mut stack = ctx.horizontal_stack((10, 1));
|
||||||
stack.insert("one");
|
stack.insert("one");
|
||||||
stack.insert("two");
|
stack.insert("two");
|
||||||
assert_eq!(stack.view.render_text(), "onetwo\0\0\0\0\n".to_string());
|
assert_eq!(stack.view.render_text(), "onetwo\0\0\0\0\n".to_string());
|
||||||
|
@ -199,7 +199,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_component() {
|
fn test_component() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.horizontal_stack((10, 2).into());
|
let mut stack = ctx.horizontal_stack((10, 2));
|
||||||
stack.component((10, 2), |ctx: &mut ViewContext| {
|
stack.component((10, 2), |ctx: &mut ViewContext| {
|
||||||
ctx.insert((3, 1), "one");
|
ctx.insert((3, 1), "one");
|
||||||
});
|
});
|
||||||
|
@ -212,7 +212,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_left() {
|
fn test_align_left() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.vertical_stack((10, 2).into());
|
let mut stack = ctx.vertical_stack((10, 2));
|
||||||
stack.component((5, 1), |ctx: &mut ViewContext| {
|
stack.component((5, 1), |ctx: &mut ViewContext| {
|
||||||
ctx.insert((0, 0), "one");
|
ctx.insert((0, 0), "one");
|
||||||
});
|
});
|
||||||
|
@ -226,7 +226,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_right() {
|
fn test_align_right() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.vertical_stack((10, 3).into());
|
let mut stack = ctx.vertical_stack((10, 3));
|
||||||
stack.alignment = StackAlignment::Right;
|
stack.alignment = StackAlignment::Right;
|
||||||
stack.insert("one");
|
stack.insert("one");
|
||||||
stack.component((5, 1), |ctx: &mut ViewContext| {
|
stack.component((5, 1), |ctx: &mut ViewContext| {
|
||||||
|
@ -248,7 +248,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_center_v() {
|
fn test_align_center_v() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.vertical_stack((10, 3).into());
|
let mut stack = ctx.vertical_stack((10, 3));
|
||||||
stack.alignment = StackAlignment::Center;
|
stack.alignment = StackAlignment::Center;
|
||||||
stack.insert("one");
|
stack.insert("one");
|
||||||
stack.component((5, 1), |ctx: &mut ViewContext| {
|
stack.component((5, 1), |ctx: &mut ViewContext| {
|
||||||
|
@ -270,7 +270,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_top() {
|
fn test_align_top() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.horizontal_stack((9, 6).into());
|
let mut stack = ctx.horizontal_stack((9, 6));
|
||||||
stack.component((3, 1), |ctx: &mut ViewContext| {
|
stack.component((3, 1), |ctx: &mut ViewContext| {
|
||||||
ctx.insert((0, 0), "one");
|
ctx.insert((0, 0), "one");
|
||||||
});
|
});
|
||||||
|
@ -292,7 +292,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_bottom() {
|
fn test_align_bottom() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.horizontal_stack((9, 6).into());
|
let mut stack = ctx.horizontal_stack((9, 6));
|
||||||
stack.alignment(StackAlignment::Bottom);
|
stack.alignment(StackAlignment::Bottom);
|
||||||
stack.component((3, 1), |ctx: &mut ViewContext| {
|
stack.component((3, 1), |ctx: &mut ViewContext| {
|
||||||
ctx.insert((0, 0), "one");
|
ctx.insert((0, 0), "one");
|
||||||
|
@ -315,7 +315,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_align_center_h() {
|
fn test_align_center_h() {
|
||||||
let ctx = crate::context::tests::context_fixture();
|
let ctx = crate::context::tests::context_fixture();
|
||||||
let mut stack = ctx.horizontal_stack((9, 6).into());
|
let mut stack = ctx.horizontal_stack((9, 6));
|
||||||
stack.alignment(StackAlignment::Center);
|
stack.alignment(StackAlignment::Center);
|
||||||
stack.component((3, 1), |ctx: &mut ViewContext| {
|
stack.component((3, 1), |ctx: &mut ViewContext| {
|
||||||
ctx.insert((0, 0), "one");
|
ctx.insert((0, 0), "one");
|
||||||
|
|
Loading…
Reference in New Issue