Skip to content

Commit

Permalink
Add flight booker example from 7gui (#280)
Browse files Browse the repository at this point in the history
* Add flight booker example from 7gui

* Add images to 7gui flight booker
  • Loading branch information
sudormrfbin authored Jan 20, 2024
1 parent 6554367 commit 4dded85
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
11 changes: 11 additions & 0 deletions examples/flight_booker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "flight_booker"
edition = "2021"
license.workspace = true
version.workspace = true

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

[dependencies]
floem = { path = "../.." }
time = { version = "0.3.31", features = ["parsing", "macros"] }
22 changes: 22 additions & 0 deletions examples/flight_booker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Flight Booker

This is an example that emulates an application that books flights, as
described in [task 3][task3] of [7gui tasks][7gui].

> The focus of Flight Booker lies on modelling constraints between
> widgets on the one hand and modelling constraints within a widget
> on the other hand. Such constraints are very common in everyday
> interactions with GUI applications. A good solution for Flight Booker
> will make the constraints clear, succinct and explicit in the source
> code and not hidden behind a lot of scaffolding.
| Initial state | Invalid date format | Return date before start date |
| ------- | ------- | ------- |
| ![valid] | ![invalid] | ![return-disabled] |

[task3]: https://eugenkiss.github.io/7guis/tasks/#flight
[7gui]: https://eugenkiss.github.io/7guis/

[valid]: https://github.com/lapce/floem/assets/23398472/fe2758d3-7161-43a3-b059-8a4a1ce0c02e
[invalid]: https://github.com/lapce/floem/assets/23398472/aeb843aa-520b-48f3-a39d-6acb414dba57
[return-disabled]: https://github.com/lapce/floem/assets/23398472/8f1268f9-efbd-4a4d-9a47-7a50425e3e39
106 changes: 106 additions & 0 deletions examples/flight_booker/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use floem::{
peniko::Color,
reactive::{create_rw_signal, create_signal},
unit::UnitExt,
view::View,
views::{dyn_container, empty, h_stack, text, v_stack, Decorators},
widgets::{button, labeled_radio_button, text_input},
};
use time::Date;

fn oneway_message(start_text: String) -> String {
format!("You have booked a one-way flight on {start_text}")
}

fn return_message(start_text: String, return_text: String) -> String {
format!("You have booked a flight on {start_text} and a return flight on {return_text}",)
}

#[derive(Eq, PartialEq, Clone)]
enum FlightMode {
OneWay,
Return,
}

static DATE_FORMAT: &[time::format_description::FormatItem<'_>] =
time::macros::format_description!("[day]-[month]-[year]");

pub fn app_view() -> impl View {
let (flight_mode, flight_mode_set) = create_signal(FlightMode::OneWay);

let start_text = create_rw_signal("24-02-2024".to_string());
let start_date = move || Date::parse(&start_text.get(), &DATE_FORMAT).ok();
let start_date_is_valid = move || start_date().is_some();

let return_text = create_rw_signal("24-02-2024".to_string());
let return_date = move || Date::parse(&return_text.get(), &DATE_FORMAT).ok();
let return_text_is_enabled = move || flight_mode.get() == FlightMode::Return;
let return_date_is_valid = move || {
if return_text_is_enabled() {
return_date().is_some()
} else {
true
}
};

let dates_are_chronological = move || match flight_mode.get() {
FlightMode::OneWay => true,
FlightMode::Return => match (return_date(), start_date()) {
(Some(ret), Some(start)) => ret >= start,
_ => false,
},
};

let did_booking = create_rw_signal(false);

let mode_picker = h_stack((
labeled_radio_button(FlightMode::OneWay, flight_mode, || "One way flight")
.on_click_stop(move |_| flight_mode_set.set(FlightMode::OneWay)),
labeled_radio_button(FlightMode::Return, flight_mode, || "Return flight")
.on_click_stop(move |_| flight_mode_set.set(FlightMode::Return)),
));

let start_date_input = text_input(start_text)
.placeholder("Start date")
.style(move |s| s.apply_if(!start_date_is_valid(), |s| s.background(Color::RED)));
let return_date_input = text_input(return_text)
.placeholder("Return date")
.style(move |s| s.apply_if(!return_date_is_valid(), |s| s.background(Color::RED)))
.disabled(move || !return_text_is_enabled());

let book_button = button(|| "Book")
.disabled(move || {
!(dates_are_chronological() && start_date_is_valid() && return_date_is_valid())
})
.on_click_stop(move |_| did_booking.set(true));

let success_message = dyn_container(
move || did_booking.get(),
move |booked| match (booked, flight_mode.get()) {
(true, FlightMode::OneWay) => Box::new(text(oneway_message(start_text.get()))),
(true, FlightMode::Return) => {
Box::new(text(return_message(start_text.get(), return_text.get())))
}
(false, _) => Box::new(empty()),
},
);

v_stack((
mode_picker,
start_date_input,
return_date_input,
book_button,
success_message,
))
.style(|s| s.gap(0, 5))
.style(|s| {
s.size(100.pct(), 100.pct())
.flex_col()
.items_center()
.justify_center()
})
}

fn main() {
floem::launch(app_view);
}

0 comments on commit 4dded85

Please sign in to comment.