A Basic Counter App
The easiest way to learn about Maycoon is to start studying some examples. In this Tutorial we will find out how to create a basic counter App with increase and decrease buttons, as well as a text widget to display the count.
Setup
For creating a new project, see the Installation Guide.
The App
First we need to import the necessary items from the maycoon
crate:
use maycoon::core::app::context::AppContext;
use maycoon::core::app::update::Update;
use maycoon::core::app::Application;
use maycoon::core::config::MayConfig;
use maycoon::core::layout::{AlignItems, Dimension, FlexDirection, LayoutStyle};
use maycoon::core::reference::Ref;
use maycoon::core::signal::eval::EvalSignal;
use maycoon::core::signal::state::StateSignal;
use maycoon::core::signal::Signal;
use maycoon::core::widget::{Widget, WidgetLayoutExt};
use maycoon::math::Vector2;
use maycoon::theme::theme::celeste::CelesteTheme;
use maycoon::widgets::button::Button;
use maycoon::widgets::container::Container;
use maycoon::widgets::text::Text;
/// The application structure.
struct MyApp;
impl Application for MyApp {
/// The theme to use.
type Theme = CelesteTheme;
/// The global state of the application.
type State = ();
fn build(context: AppContext, _: Self::State) -> impl Widget {
let counter = context.use_signal(StateSignal::new(0));
Container::new(vec![
{
let counter = counter.clone();
Box::new(
Button::new(Text::new("Increase".to_string())).with_on_pressed(
EvalSignal::new(move || {
counter.set(*counter.get() + 1);
Update::DRAW
})
.hook(&context)
.maybe(),
),
)
},
{
let counter = counter.clone();
Box::new(
Button::new(Text::new("Decrease".to_string())).with_on_pressed(
EvalSignal::new(move || {
counter.set(*counter.get() - 1);
Update::DRAW
})
.hook(&context)
.maybe(),
),
)
},
Box::new(Text::new(counter.map(|i| Ref::Owned(i.to_string())))),
])
.with_layout_style(LayoutStyle {
size: Vector2::<Dimension>::new(Dimension::percent(1.0), Dimension::percent(1.0)),
flex_direction: FlexDirection::Column,
align_items: Some(AlignItems::Center),
..Default::default()
})
}
/// The configuration of the application.
fn config(&self) -> MayConfig<Self::Theme> {
MayConfig::default()
}
}
fn main() {
/// Run the application without a real global state.
MyApp.run(())
}
A Container
widget draws and handles a collection of widgets specified as a Vector of Box
ed Widgets. In our case, we need two buttons: Increase and decrease to manipulate our counter value, as well as a text widget for displaying the counter value.
We use the MaybeSignal::signal
function to pass the signals to the buttons and pass fixed strings to the "Increase" and "Decrease" buttons.
For the two buttons, we need to define Update
s to apply updates to the App.
The Update::DRAW
constant tells the App to only re-draw and not re-layout the application when pressing the buttons.
The with_layout_style
function applies a custom layout which centers the widgets in our case.
NOTE: You need to clone
signals before using them inside move
closures to use them inside multiple closures.
Running the App
To launch the App, you can run cargo run
.
You should see a window with "Increase" and "Decrease" buttons along with a text.