Skip to content

Basic GUI support #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 82 commits into from
Jun 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
951e323
Draft UI API and create an examples menu [ci skip]
hecrj May 8, 2019
17cb31b
Merge branch 'master' into feature/ui
hecrj May 9, 2019
7064222
Start integrating `stretch` for UI layouting
hecrj May 10, 2019
f301490
Draft `Node`, `Root`, `Layout`, and `Style`
hecrj May 10, 2019
f9d5679
Support generic UI renderers :tada:
hecrj May 11, 2019
f71b13e
Render columns outline for now
hecrj May 11, 2019
d2c1279
Set up basic UI event propagation
hecrj May 12, 2019
1d5477f
Refactor `Game` trait to ease UI support
hecrj May 13, 2019
6e1c0aa
Render button text
hecrj May 14, 2019
edac5c0
Implement mouse cursor control and event triggering
hecrj May 15, 2019
8f8287a
Make `Widget` trait explicitly recursive
hecrj May 16, 2019
a78572d
Implement `ui::Map`
hecrj May 16, 2019
acdbfcf
Draft basic `ui::Text` widget
hecrj May 18, 2019
1a23d1f
Use reference for `content` in `graphics::Text`
hecrj May 19, 2019
7c137bf
Draft `ui::Panel` implementation.
hecrj May 19, 2019
55e909c
Fix some warnings
hecrj May 19, 2019
7c29c69
Implement `Row` and rethink UI example
hecrj May 21, 2019
a7d5a1d
Add alignment support for `graphics::Text`
hecrj May 21, 2019
bbba5e2
Complete text alignment support for wgpu backend
hecrj May 21, 2019
1f9ac93
Render `ui::Button` properly
hecrj May 21, 2019
064beb3
Implement `ui::button::Type`
hecrj May 22, 2019
a318fda
Provide default `Widget::on_event`
hecrj May 22, 2019
058d04b
Implement hash-based UI cache :tada:
hecrj May 23, 2019
70ca726
Implement `Element` and `Checkbox`.
hecrj May 24, 2019
cd0c650
Implement radio button and organize renderer modules
hecrj May 24, 2019
d448069
Implement `Slider` widget
hecrj May 26, 2019
925cdad
Implement `Slider` mouse cursor feedback
hecrj May 26, 2019
68ae1e1
Remove unused imports
hecrj May 26, 2019
5741d01
Merge branch 'master' into feature/ui
hecrj May 26, 2019
26bb9fc
Remove unnecessary `Widget` lifetime
hecrj May 26, 2019
436465b
Replace `Root` with `Element`
hecrj May 26, 2019
0df9fb0
Complete UI example
hecrj May 27, 2019
d519db9
Rename `UserInterface::Event` to `UserInterface::Message`
hecrj May 29, 2019
923102a
Restructure UI concepts based on simplicity.
hecrj May 29, 2019
b321247
Merge branch 'master' into feature/ui
hecrj May 31, 2019
1ce4fae
Merge branch 'master' into feature/ui
hecrj May 31, 2019
803f39f
Use flexbox alignment concepts for now
hecrj Jun 1, 2019
b45f429
Embed default UI spritesheet instead of loading it from the filesystem
hecrj Jun 2, 2019
d645830
Allow renderer configuration (sprites and font)
hecrj Jun 2, 2019
669e3aa
Use a checkbox to toggle graphics interpolation in the particles example
hecrj Jun 2, 2019
cb6dd61
Implement cursor availability input events.
hecrj Jun 2, 2019
55c2cd6
Start `ui` module documentation
hecrj Jun 4, 2019
a28b3ae
Implement `counter` example
hecrj Jun 4, 2019
1fe17a7
Remove debug code from UI renderer
hecrj Jun 4, 2019
59649a1
Make `column` module private.
hecrj Jun 4, 2019
5df1c99
Write docs for `ui::core::Element`
hecrj Jun 4, 2019
c6f4dec
Write docs for `ui::core::Layout`
hecrj Jun 5, 2019
31a35c1
Rename `Node::new_leaf` to `Node::with_measure`
hecrj Jun 5, 2019
e4d8781
Write docs for `ui::core::Style`.
hecrj Jun 5, 2019
e35b0d0
Write docs for `ui::core::{Align, Justify}`
hecrj Jun 5, 2019
cc8eecd
Write docs for the rest of `ui::core`.
hecrj Jun 5, 2019
06c292b
Expose the correct `Node::new` method
hecrj Jun 5, 2019
8ccea5f
Write docs for `Column` and `Row`
hecrj Jun 6, 2019
2c316d3
Write docs for `widget::button`
hecrj Jun 6, 2019
65c9a10
Remove type parameter `R` from most widgets.
hecrj Jun 6, 2019
a5ae9db
Write docs for `widget::checkbox`
hecrj Jun 6, 2019
53515fe
Write docs for `widget::radio`
hecrj Jun 6, 2019
d916000
Move `ui::core::widget` to `ui::widget`.
hecrj Jun 7, 2019
9f4a5f9
Make `KeyboardAndMouse::clicks` return a slice
hecrj Jun 7, 2019
92aa6b6
Write docs for `ui::widget::text`
hecrj Jun 7, 2019
08fce0d
Write docs for `ui:core::Widget`
hecrj Jun 7, 2019
309c5e6
Write docs for `ui::{Renderer, Configuration}`
hecrj Jun 7, 2019
348df4d
Update outdated docs
hecrj Jun 7, 2019
23af72b
Fix `debug::null`
hecrj Jun 7, 2019
25f1a59
Update README
hecrj Jun 7, 2019
f6da2c4
Warn users about breaking changes in `master`
hecrj Jun 7, 2019
7ab3377
Add missing docs and debug implementations
hecrj Jun 8, 2019
f9cdb8a
Disable cache in CI and update READMEs
hecrj Jun 8, 2019
e4cee8c
Remove `HasCursorPosition` trait.
hecrj Jun 8, 2019
26d5176
Improve `Input` trait documentation
hecrj Jun 8, 2019
7eaf8f3
Fix some documentation inconsistencies
hecrj Jun 8, 2019
76f1cdd
Finish introduction to the `ui` module
hecrj Jun 8, 2019
b4527c1
Finish `ui` documentation :tada:
hecrj Jun 8, 2019
bda9818
Move `resources/ui` to `images/ui`
hecrj Jun 8, 2019
3450937
Update image sources in documentation
hecrj Jun 8, 2019
00517f1
Remove too much `simply` reiteration
hecrj Jun 8, 2019
827c2a2
Fix inconsistent punctuation
hecrj Jun 8, 2019
1e281f4
Fix minor doc details
hecrj Jun 8, 2019
8c32f37
Remove `State` trait and simplify everything
hecrj Jun 9, 2019
88044e6
Update `CHANGELOG`
hecrj Jun 9, 2019
104c065
Fix `CHANGELOG`
hecrj Jun 9, 2019
a5d2647
Include user interface section in examples `README`
hecrj Jun 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ os:
- linux
- osx
- windows
cache: cargo
before_install:
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
Expand Down
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Added
- __Responsive GUI support!__ The new `ui` module can be used to extend a `Game`
and build a user interface. [#35]
- GUI runtime based on [Elm] and [The Elm Architecture].
- Layouting based on Flexbox and powered by [`stretch`].
- Built-in GUI widgets. Specifically: buttons, sliders, checkboxes, radio
buttons, rows, and columns.
- Built-in GUI renderer. It is capable of rendering all the built-in GUI
widgets.
- Customization. The `ui::core` module can be used to implement custom widgets
and renderers.
- `Input` trait. It allows to implement reusable input handlers. [#35]
- `KeyboardAndMouse` input handler. Useful to quickstart development and have
easy access to the keyboard and the mouse from the get-go. [#35]
- `CursorTaken` and `CursorReturned` input events. They are fired when the cursor
is used/freed by the user interface.
- Off-screen text rendering support. `Font::draw` now supports any `Target`
instead of a window `Frame`. [#25]
- `Game::debug` performance tracking. Time spent on this method is now shown in
Expand All @@ -18,17 +33,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implementation of `ParallelExtend` for `Batch`. A `Batch` can now be populated
using multiple threads, useful to improve performance when dealing with many
thousands of quads. [#37]
- `Text` alignment. It can be defined using the new `HorizontalAlignment` and
- `VerticalAlignment` types in the `graphics` module. [#35]
- `Font::measure`. It allows to measure the dimensions of any `Text`. [#35]
- `Rectangle::contains`. It returns whether or not a `Rectangle` contains a
given `Point`. [#35]
- `Sprite::scale`. It can be used to change the `Sprite` size when drawed.
- `Default` implementation for `Sprite`. [#35]
- `Debug::ui_duration`. It returns the average time spent running the UI runtime.
- Multiple gravity centers based on mouse clicks in the particles example. [#30]

### Changed
- The `View` associated type has been removed. Thus, implementors of the `Game`
trait are also meant to hold the game assets. This simplifies the API
considerably, and it helps model your game state-view relationship with
precision, avoiding inconsistencies. [#35]
- The `Game::Input` associated type now has to implement the new `Input` trait.
This splits code quite nicely, as the `on_input` method moves away from `Game`.
It also makes `Input` implementors reusable. For instance, a `KeyboardAndMouse`
type has been implemented that can be used out of the box! [#35]
- The `Game::LoadingScreen` associated type has been introduced. Given that all
the `Game` associated types implement a trait with a `load` method, wiring a
loading screen now is as simple as writing its name. Because of this, the
`Game::new` method is no longer necessary and it is dropped. [#35]
- `Game::draw` now takes a `Frame` directly instead of a `Window`. [#35]
- `LoadingScreen::on_progress` has been renamed to `LoadingScreen::draw` and it
now receives a `Frame` instead of a `Window`. [#35]
- The performance of the particles example has been improved considerably on all
platforms. [#37]

### Removed
- `Game::new`. `Game::load` should be used instead. [#35]
- `Game::on_input`. Input handlers now must be implemented using the new `Input`
trait. [#35]

[#25]: https://github.com/hecrj/coffee/pull/25
[#26]: https://github.com/hecrj/coffee/pull/26
[#28]: https://github.com/hecrj/coffee/pull/28
[#30]: https://github.com/hecrj/coffee/pull/30
[#35]: https://github.com/hecrj/coffee/pull/35
[#37]: https://github.com/hecrj/coffee/pull/37
[Elm]: https://elm-lang.org
[The Elm Architecture]: https://guide.elm-lang.org/architecture/
[`stretch`]: https://github.com/vislyhq/stretch


## [0.2.0] - 2019-04-28
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ debug = []
image = "0.21"
nalgebra = "0.18"
rayon = "1.0"
stretch = "0.2"
twox-hash = "1.3"

# gfx (OpenGL)
gfx = { version = "0.18", optional = true }
Expand Down
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bugs. [Feel free to contribute!]
[Feel free to contribute!]: #contributing--feedback

## Features
* Responsive, customizable GUI with built-in widgets
* Declarative, type-safe asset loading
* Loading screens with progress tracking
* Built-in [debug view with performance metrics]
Expand Down Expand Up @@ -49,43 +50,43 @@ performance:
opt-level = 2
```

__Coffee moves fast and the `master` branch can contain breaking changes!__ If
you want to learn about a specific release, check out [the release list].

[the release list]: https://github.com/hecrj/coffee/releases

## Overview
Here is a minimal example that will open a window:

```rust
use coffee::graphics::{Color, Frame, Window, WindowSettings};
use coffee::load::Task;
use coffee::{Game, Result, Timer};
use coffee::graphics::{Color, Window, WindowSettings};

fn main() -> Result<()> {
MyGame::run(WindowSettings {
title: String::from("A caffeinated game"),
size: (1280, 1024),
resizable: true,
fullscreen: false,
})
}

struct MyGame {
// Your game state goes here...
// Your game state and assets go here...
}

impl Game for MyGame {
type View = (); // No view data.
type Input = (); // No input data.

const TICKS_PER_SECOND: u16 = 60; // Update rate
type Input = (); // No input data
type LoadingScreen = (); // No loading screen

fn new(_window: &mut Window) -> Result<(MyGame, Self::View, Self::Input)> {
fn load(_window: &Window) -> Task<MyGame> {
// Load your game assets here. Check out the `load` module!
Ok((MyGame { /* ... */ }, (), ()))
}

fn update(&mut self, _view: &Self::View, _window: &Window) {
// Update your game here
Task::new(|| MyGame { /* ... */ })
}

fn draw(&self, _view: &mut Self::View, window: &mut Window, _timer: &Timer) {
fn draw(&mut self, frame: &mut Frame, _timer: &Timer) {
// Clear the current frame
let mut frame = window.frame();
frame.clear(Color::BLACK);

// Draw your game here. Check out the `graphics` module!
Expand All @@ -104,13 +105,15 @@ Coffee builds upon
* [`winit`] for windowing and mouse/keyboard events.
* [`gfx` pre-ll] for OpenGL support, based heavily on the [`ggez`] codebase.
* [`wgpu`] for _experimental_ Vulkan, Metal, D3D11 and D3D12 support.
* [`stretch`] for responsive GUI layouting based on Flexbox.
* [`glyph_brush`] for TrueType font rendering.
* [`nalgebra`] for the `Point`, `Vector`, and `Transformation` types.
* [`image`] for image loading and texture array building.

[`winit`]: https://github.com/rust-windowing/winit
[`gfx` pre-ll]: https://github.com/gfx-rs/gfx/tree/pre-ll
[`wgpu`]: https://github.com/gfx-rs/wgpu
[`stretch`]: https://github.com/vislyhq/stretch
[`glyph_brush`]: https://github.com/alexheretic/glyph-brush/tree/master/glyph-brush
[`nalgebra`]: https://github.com/rustsim/nalgebra
[`image`]: https://github.com/image-rs/image
Expand All @@ -134,7 +137,10 @@ the [Rust Community Discord]. I go by `@lone_scientist` there.

## Credits / Thank you
* [`ggez`], an awesome, easy-to-use, good game engine that introduced me to
Rust a month ago. Its graphics implementation served me as a guide to
implement OpenGL support for Coffee.
Rust. Its graphics implementation served me as a guide to implement OpenGL
support for Coffee.
* [Kenney], creators of amazing free game assets with no strings attached. The
built-in GUI renderer in Coffee uses a modified version of their UI sprites.

[`ggez`]: https://github.com/ggez/ggez
[Kenney]: https://kenney.nl
16 changes: 16 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ OpenGL, we run:
cargo run --example <example> --features opengl
```

__Coffee moves fast and the `master` branch can contain breaking changes!__ If
you want to learn about a specific release, check out [the release list].

[the release list]: https://github.com/hecrj/coffee/releases

## Particles

A particle gravity simulator that showcases a loading screen, input handling,
Expand All @@ -26,3 +31,14 @@ cargo run --example particles --features opengl,debug --release
![Particles example][particles]

[particles]: https://github.com/hecrj/coffee/blob/master/images/examples/particles.png?raw=true

## User Interface

A tour showcasing the different built-in widgets available for building
responsive user interfaces in Coffee.

```
cargo run --example ui --features opengl,debug --release
```

[![GUI](https://thumbs.gfycat.com/LivelyOnlyHypacrosaurus-size_restricted.gif)](https://gfycat.com/livelyonlyhypacrosaurus)
83 changes: 36 additions & 47 deletions examples/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use coffee::graphics::{
Color, Font, Frame, Image, Point, Quad, Rectangle, Text, Window,
WindowSettings,
};
use coffee::load::{loading_screen, Join, LoadingScreen, Task};
use coffee::load::{loading_screen::ProgressBar, Join, Task};
use coffee::{Game, Result, Timer};

fn main() -> Result<()> {
Expand All @@ -14,31 +14,47 @@ fn main() -> Result<()> {
})
}

struct Colors;

impl Game for Colors {
type View = View;
type Input = ();
struct Colors {
palette: Image,
font: Font,
}

const TICKS_PER_SECOND: u16 = 10;
impl Colors {
const PRUSSIAN_BLUE: Color = Color {
r: 0.0,
g: 0.1922,
b: 0.3255,
a: 1.0,
};

fn new(window: &mut Window) -> Result<(Self, Self::View, Self::Input)> {
let load = Task::stage("Loading view...", View::load());
fn load() -> Task<Colors> {
(
Task::using_gpu(|gpu| {
Image::from_colors(gpu, &[Self::PRUSSIAN_BLUE])
}),
Font::load(include_bytes!(
"../resources/font/Inconsolata-Regular.ttf"
)),
)
.join()
.map(|(palette, font)| Colors { palette, font })
}
}

let mut loading_screen = loading_screen::ProgressBar::new(window.gpu());
let view = loading_screen.run(load, window)?;
impl Game for Colors {
type Input = ();
type LoadingScreen = ProgressBar;

Ok((Colors, view, ()))
fn load(_window: &Window) -> Task<Self> {
Task::stage("Loading view...", Colors::load())
}

fn update(&mut self, _view: &Self::View, _window: &Window) {}

fn draw(&self, view: &mut Self::View, frame: &mut Frame, _timer: &Timer) {
fn draw(&mut self, frame: &mut Frame, _timer: &Timer) {
frame.clear(Color::new(0.5, 0.5, 0.5, 1.0));

let target = &mut frame.as_target();

view.palette.draw(
self.palette.draw(
Quad {
source: Rectangle {
x: 0.0,
Expand All @@ -52,41 +68,14 @@ impl Game for Colors {
target,
);

view.font.add(Text {
content: String::from("Prussian blue"),
self.font.add(Text {
content: "Prussian blue",
position: Point::new(20.0, 500.0),
size: 50.0,
color: View::PRUSSIAN_BLUE,
color: Self::PRUSSIAN_BLUE,
..Text::default()
});

view.font.draw(target);
}
}

struct View {
palette: Image,
font: Font,
}

impl View {
const PRUSSIAN_BLUE: Color = Color {
r: 0.0,
g: 0.1922,
b: 0.3255,
a: 1.0,
};

fn load() -> Task<View> {
(
Task::using_gpu(|gpu| {
Image::from_colors(gpu, &[Self::PRUSSIAN_BLUE])
}),
Font::load(include_bytes!(
"../resources/font/Inconsolata-Regular.ttf"
)),
)
.join()
.map(|(palette, font)| View { palette, font })
self.font.draw(target);
}
}
Loading