The Final Code

Rust

use vizia::prelude::*;

// Define the application data
#[derive(Lens)]
pub struct AppData {
    count: i32,
}

// Define events for mutating the application data
pub enum AppEvent {
    Increment,
    Decrement,
}

// Mutate application data in response to events
impl Model for AppData {
    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
        event.map(|app_event, meta| match app_event {
            AppEvent::Decrement => self.count -= 1,
            AppEvent::Increment => self.count += 1,
        });
    }
}

// Define a custom view for the counter
pub struct Counter {
    on_increment: Option<Box<dyn Fn(&mut EventContext)>>,
    on_decrement: Option<Box<dyn Fn(&mut EventContext)>>,
}

impl Counter {
    pub fn new<L>(cx: &mut Context, lens: L) -> Handle<Self>
    where
        L: Lens<Target = i32>
    {
        Self {
            on_decrement: None,
            on_increment: None,
        }.build(cx, |cx|{
            HStack::new(cx, |cx|{
                Button::new(cx, |cx| Label::new(cx, Localized::new("dec")))
                    .on_press(|ex| ex.emit(CounterEvent::Decrement))
                    .class("dec");

                Button::new(cx, |cx| Label::new(cx, Localized::new("inc")))
                    .on_press(|ex| ex.emit(CounterEvent::Increment))
                    .class("inc");
                
                Label::new(cx, AppData::count)
                    .class("count")
                    .live(Live::Assertive);
            })
            .class("row");
        })
    }
}

// Internal events
pub enum CounterEvent {
    Decrement,
    Increment,
}

// Handle internal events
impl View for Counter {
    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
        event.map(|counter_event, meta| match counter_event{
            CounterEvent::Increment => {
                if let Some(callback) = &self.on_increment {
                    (callback)(cx);
                }
            }

            CounterEvent::Decrement => {
                if let Some(callback) = &self.on_decrement {
                    (callback)(cx);
                }
            }
        });
    }
}

// Custom modifiers
pub trait CounterModifiers {
    fn on_increment<F: Fn(&mut EventContext) + 'static>(self, callback: F) -> Self;
    fn on_decrement<F: Fn(&mut EventContext) + 'static>(self, callback: F) -> Self;
}

// Implement custom modifiers
impl<'a> CounterModifiers for Handle<'a, Counter> {
    fn on_increment<F: Fn(&mut EventContext) + 'static>(self, callback: F) -> Self {
        self.modify(|counter| counter.on_increment = Some(Box::new(callback)))
    }

    fn on_decrement<F: Fn(&mut EventContext) + 'static>(self, callback: F) -> Self {
        self.modify(|counter| counter.on_decrement = Some(Box::new(callback)))
    }
}

fn main() {
    Application::new(|cx|{

        cx.add_stylesheet(include_style!("src/style.css")).expect("Failed to load stylesheet");

        cx.add_translation(
            langid!("en-US"),
            include_str!("resources/en-US/counter.ftl").to_owned(),
        );

        cx.add_translation(
            langid!("es"),
            include_str!("resources/es/counter.ftl").to_owned(),
        );

        // Uncomment to test with spanish locale.
        // If system locale is already Spanish, replace "es" with "en-US".
        // cx.emit(EnvironmentEvent::SetLocale(langid!("es")));

        // Build model data into the application
        AppData { count: 0 }.build(cx);

        // Add the custom counter view and bind to the model data
        Counter::new(cx, AppData::count)
            .on_increment(|cx| cx.emit(AppEvent::Increment))
            .on_decrement(|cx| cx.emit(AppEvent::Decrement));
    })
    .title("Counter")
    .inner_size((400, 150))
    .run();
}

CSS

.row {
    child-space: 1s;
    col-between: 20px;
}

button {
    border-width: 0px;
}

button.dec {
    background-color: rgb(170, 50, 50);
}

button.inc {
    background-color: rgb(50, 170, 50);
}

label.count {
    child-space: 1s;
    border-width: 1px;
    border-color: #808080;
    border-radius: 4px;
    width: 50px;
    height: 32px;
}

Fluent

resources/en-US/counter.ftl

inc = Increment
dec = Decrement

resources/es/counter.ftl

inc = Incrementar
dec = Decrementar