Skip to content
This repository has been archived by the owner on Dec 29, 2023. It is now read-only.

Commit

Permalink
Implement new documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
pieterdd committed Dec 22, 2023
1 parent b5df4b5 commit bb971c4
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 141 deletions.
6 changes: 4 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"rust-analyzer.linkedProjects": [
".\\examples\\showcase\\Cargo.toml",
".\\Cargo.toml"
".\\Cargo.toml",
".\\examples\\counter_floem\\Cargo.toml",
".\\examples\\counter_prettygooey\\Cargo.toml",
".\\examples\\showcase\\Cargo.toml"
]
}
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ strum = { version = "0.25.0", features = ["derive"] }

[workspace]
members = [
"examples/counter_floem",
"examples/counter_prettygooey",
"examples/showcase",
]
162 changes: 25 additions & 137 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,165 +1,53 @@
# Prettygoey
_The discontinued Iced-based version of Prettygooey can still be found [here](https://github.com/pieterdd/prettygooey/tree/0.1.1)._

# Prettygooey

[![Crates.io](https://img.shields.io/crates/v/prettygooey.svg)](https://crates.io/crates/prettygooey)
[![docs.rs](https://img.shields.io/docsrs/prettygooey/latest)](https://docs.rs/prettygooey/latest/prettygooey/theme/struct.Theme.html)

Prettygooey is a set of themed UI components for the [iced](https://iced.rs/) GUI library. Works on Windows, Linux and Mac.
Prettygooey is a set of themed UI components for the [floem](https://github.com/lapce/floem) GUI library. Works on Windows, Linux and Mac.

![Showcase](docs/img/showcase.png)

⚠️ **Prettygooey, like iced, is experimental software.** ⚠️

## First time using iced?

The [official iced book](https://book.iced.rs/) was fairly short at the time of writing, so my limited knowledge was pieced together from [docs.rs](https://docs.rs/iced/latest/iced/index.html) snippets, reading source code and visiting their [forums](https://discourse.iced.rs/).

The simplest way to create a GUI in iced is with an [`iced::Sandbox`](https://docs.rs/iced/latest/iced/trait.Sandbox.html). It kind of behaves like a window. You'll have to implement four methods:

- `new`: prepares your initial state
- `title`: controls the title of the window
- `update`: more on this later
- `view`: renders your widgets

### Thinking in widgets

Iced, like many GUI frameworks, works with containers (i.e. groups of widgets) and widgets. Containers help you lay out your widgets, and common examples of them include a row or a column. Containers can contain containers, too.

Suppose you want to create a confirmation dialog. Such a layout would probably have the following structure:

- A column container
- A text widget with label "Are you sure?"
- A row container
- A "Yes" button
- A "No" button

When using vanilla iced widgets, this would look something like this:

![Vanilla iced example](docs/img/vanilla_iced.png)

Row and column containers have some useful modifier functions:

- `spacing` adds a gap between your widgets to let your layout breathe.
- `padding` adds spacing on the outside of the container. This keeps your text from sticking to the side of the window.
⚠️ **Prettygooey, like floem, is experimental software.** ⚠️

Here's a simple example putting the above into practice:
## First time using floem?

```rust
fn view(&self) -> Element<'_, Self::Message> {
column!(
text("Are you sure?"),
row![button("Yes"), button("No")].spacing(10)
)
.spacing(20)
.padding(10)
.into()
}
```
Floem's documentation is available [here](http://lapce.dev/floem/floem/). You might notice that it incorporates familiar concepts from other UI frameworks, including:

### Let's run this thing
- Interface composition using nestable horizontal and vertical widget containers
- A styling system that brings many CSS-like features to Rust, without the overhead of a browser runtime
- Automatic propagation of data changes to your UI using signals, as seen in [SolidJS](https://www.solidjs.com/)
- Implementation of UIs using functions, similar to Jetpack Compose
- Keyboard accessibility

You've defined your UI, but it won't show up until you invoke it. In the example below, we initialize a window with mostly default settings.
For a mimimal example involving a counter that can be incremented/decremented, have a look at [this file](examples/counter_floem/src/main.rs). It looks quite basic when rendered:

```rust
MySandbox::run(Settings {
window: window::Settings {
size: (400, 450),
position: Position::Centered,
..window::Settings::default()
},
..Settings::default()
})
```
![A counter in pure floem](docs/img/counter_floem.png)

You can find the full list of available settings [here](https://docs.rs/iced/latest/iced/settings/struct.Settings.html).

### Interactivity

Many widgets allow for some kind of user interaction. Examples include your garden variety button, text input or checkbox. These interactions produce events. For a checkbox, a typical interaction plays out like this:

1. The checkbox takes its current state from a variable
2. User clicks the checkbox
3. The checkbox emits an event containing the new state
4. Your Sandbox's `update` function reacts to that new state by updating the variable to the new state

You get to decide what the event looks like. Your Sandbox can designate a specific enum as its [Message type](https://docs.rs/iced/latest/iced/trait.Sandbox.html#associatedtype.Message). For example:

```rust
#[derive(Debug, Clone)]
enum SpaceshipSandboxMessage {
BoostersEnabledChanged(bool),
ShieldSelectionChanged(Shield),
CommanderNameChanged(String),
RebootButtonPressed,
}
```

In the case of a [checkbox](https://docs.rs/iced/latest/iced/widget/fn.checkbox.html), you set the event as follows:

```rust
let chk_enable_boosters = checkbox(
"Enable boosters",
self.enable_boosters,
Self::Message::BoostersEnabledChanged,
);
```

Your Sandbox's `update` function will be called whenever such an event is emitted. There you can respond accordingly:

```rust
fn update(&mut self, message: Self::Message) {
match message {
Self::Message::BoostersEnabledChanged(value) => {
self.enable_boosters = value;
}
}
}
```
If you've familiarized yourself with the floem's basics, keep reading to find out how to spice things up.

## Getting started with Prettygooey

In your Sandbox's constructor, create an instance of [`prettygooey::theme::Theme`](https://docs.rs/prettygooey/latest/prettygooey/theme/struct.Theme.html). You'll use this object to instantiate Prettygooey widgets.

```rust
fn new() -> Self {
Self {
theme: Theme {
accent_color: AccentColor::Magenta,
},
}
}
```

In your Sandbox's `view` function, return an instance of our primary container to apply the background.

```rust
fn view(&self) -> Element<'_, Self::Message> {
// Pass a row or column of widgets to the container
self.theme.primary_container(column![]).into()
}
```

All supported widgets can be created via the Theme instance.
The Prettygooey workflow involves:

```rust
let button = self.theme.button("Click me");
```
1. Creating an instance of [`prettygooey::theme::Theme`](https://docs.rs/prettygooey/latest/prettygooey/theme/struct.Theme.html). `Theme::default()` lets you use default settings.
2. Using the widget creation methods in `Theme` to build your UI. If Prettygooey doesn't have exactly what you want, you can mix and match with self-written UI components. You can keep using floem's `v_stack` and `h_stack` methods to lay out your components.
3. Wrapping your window contents in Prettygooey's padded container to ensure your widgets don't stick to the side of the window.
4. Wrapping the padded container in Prettygooey's primary container to apply the theme's window background.

Prettygooey widgets may provide optional customizations. Take the Text widget for example. By importing the extension trait [`prettygooey::theme::TextExt`](https://docs.rs/prettygooey/latest/prettygooey/theme/trait.TextExt.html), you expose a `variant` method that'll let you switch the variant to Dimmed:
[Over here](examples/counter_prettygooey/src/main.rs) you'll find the above example ported to Prettygooey. Here is a reference render:

```rust
self.theme
.text("Theme selection")
.variant(TextVariant::Dimmed),
```
![A counter in Prettygooey](docs/img/counter_prettygooey.png)

## Where to go from here

For a list of available widgets with screenshots and examples, see the [code docs](https://docs.rs/prettygooey/latest/prettygooey/theme/struct.Theme.html#implementations).

If you'd like to see more than a few loose snippets, check out the [showcase example](https://github.com/pieterdd/prettygooey/blob/main/examples/showcase/src/main.rs). It's the source code for the screenshot at the top of the README.
If you'd like to see more than a few loose snippets, check out the [showcase example](examples/showcase/src/main.rs). It's the source code for the screenshot at the top of the README.

## Not quite what you're looking for?

Pop! OS's [libcosmic](https://github.com/pop-os/libcosmic) implements a much wider range of iced widgets and is backed by device manufacturer System76. On the other hand, libcosmic's learning curve may be a bit steeper. The library is primarily intended for use by applications that are native to the Cosmic desktop environment.
I'm not aware of any other floem UI component libraries right now. As far as Rust UI libraries go, [Iced](https://iced.rs/) is a well-known one. Device manufacturer System76 is using it to implement its own desktop environment. You might be able to build onto their work by checking out [libcosmic](https://github.com/pop-os/libcosmic). Be advised that its learning curve may be a bit steeper than floem, and that the library is primarily intended for use by applications that are native to the Cosmic desktop environment.

That said, I'd still check it out and make up your own mind.
If you're willing to consider Electron-like solutions that may have a larger resource footprint, I'd definitely check out [Tauri](https://tauri.app/). Since it's built on HTML/JS/CSS, you can use it with any web-based UI framework.
Binary file added docs/img/counter_floem.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/counter_prettygoey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/showcase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions examples/counter_floem/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "pure-floem"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
prettygooey = { path = "../.." }
floem = { git = "https://github.com/lapce/floem", rev = "92ba6406b2406e4223933267137229db0a619a0a" }
31 changes: 31 additions & 0 deletions examples/counter_floem/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use floem::reactive::create_signal;
use floem::view::View;
use floem::views::{h_stack, label, text, v_stack, Decorators};
use floem::EventPropagation;

/// Renders your UI, and updates it whenever a signal fires
fn app_view() -> impl View {
// Use counter.get() to access the counter value.
// Change it with set_counter.update(..) or set_counter.set().
let (counter, set_counter) = create_signal(0);

// v_stack = vertical group layout
v_stack((
label(move || format!("Value: {}", counter.get())),
// h_stack = horizontal group layout
h_stack((
text("Increment").on_click(move |_| {
set_counter.update(|value| *value += 1);
EventPropagation::Stop
}),
text("Decrement").on_click(move |_| {
set_counter.update(|value| *value -= 1);
EventPropagation::Stop
}),
)),
))
}

fn main() {
floem::launch(app_view);
}
10 changes: 10 additions & 0 deletions examples/counter_prettygooey/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
prettygooey = { path = "../.." }
floem = { git = "https://github.com/lapce/floem", rev = "92ba6406b2406e4223933267137229db0a619a0a" }
33 changes: 33 additions & 0 deletions examples/counter_prettygooey/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use floem::reactive::create_signal;
use floem::view::View;
use floem::views::{h_stack, v_stack, Decorators};
use floem::EventPropagation;
use prettygooey::label::LabelVariant;
use prettygooey::theme::Theme;

fn app_view() -> impl View {
let theme = Theme::default();

let (counter, set_counter) = create_signal(0);

theme.primary_container(theme.padded_container(v_stack((
theme.label(
move || format!("Value: {}", counter.get()),
LabelVariant::Regular,
),
h_stack((
theme.button(|| "Increment").on_click(move |_| {
set_counter.update(|value| *value += 1);
EventPropagation::Stop
}),
theme.button(|| "Decrement").on_click(move |_| {
set_counter.update(|value| *value -= 1);
EventPropagation::Stop
}),
)),
))))
}

fn main() {
floem::launch(app_view);
}
2 changes: 1 addition & 1 deletion examples/showcase/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn main() {
width: 400.0,
height: 500.0,
})
.title("Prettygoeey Showcase");
.title("Prettygooey Showcase");

floem::Application::new()
.window(move |_| app_view(), Some(window_config))
Expand Down
2 changes: 1 addition & 1 deletion src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct Theme {
/// - Read the value of this field in your own code whenever you prefer
/// to manually insert horizontal window margin.
///
/// Decorative UI elements containing text (such as Prettygoeey's own header)
/// Decorative UI elements containing text (such as Prettygooey's own header)
/// may hook in to this field for the symmetry purposes.
pub horizontal_window_margin: f32,
}
Expand Down

0 comments on commit bb971c4

Please sign in to comment.