#![allow(clippy::match_same_arms)]
mod definitions;
mod hardware_input_input_line;
mod mock_data;
mod settings;
mod table;
use std::process::exit;
use std::time::{Duration, Instant};
use buoyant::{
app::{App, Harness},
event::{Event, Key, simulator::MouseTracker},
focus::{self, BoundaryBehavior, FocusAction, Role},
render_target::{EmbeddedGraphicsRenderTarget, RenderTarget},
view::prelude::*,
};
use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use embedded_graphics_simulator::{
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
};
use crate::{
definitions::{GoodPixelColor, Page, PageAction, RenderData, State},
mock_data::{PAGE_1, PAGE_2, SETTINGS},
};
const FONT: u8g2_fonts::FontRenderer =
u8g2_fonts::FontRenderer::new::<u8g2_fonts::fonts::u8g2_font_t0_13_tf>();
fn root_view(state: &State) -> impl View<Rgb888, State> + use<> {
let page = state.page;
let render_data = RenderData {
palette: &PALETTE,
page,
};
let save_settings = |app_state: &State| {
println!("Saving settings");
println!(" IP: {}", app_state.static_ip);
println!(" Gateway: {}", app_state.gateway);
println!(" Net Mask: {}", app_state.net_mask);
println!(" DNS: {}", app_state.dns);
println!(" DHCP: {}", app_state.dhcp);
};
view(render_data, state, save_settings)
}
pub fn view<'a, 'b, C: GoodPixelColor, F: Fn(&State) + 'a + Copy>(
data: RenderData<'a, C>,
state: &'b State,
save_settings: F,
) -> impl View<C, State> + use<'a, C, F> {
let paginate = move |s: &mut State, a: buoyant::view::paginate::PageEvent| {
s.page_action = Some(match a {
buoyant::view::paginate::PageEvent::Next => definitions::PageAction::Next,
buoyant::view::paginate::PageEvent::Previous => definitions::PageAction::Prev,
});
s.opened_input = None;
s.opened_cell_input = None;
s.focused_table = false;
};
let state = state.clone();
buoyant::view::Paginate::new(focus::GROUP_1, paginate, {
buoyant::match_view!(data.page, {
Page::IeTable {
header,
footer,
names,
ie,
eu,
table_dimensions: (r, c),
} => VStack::new((
hardware_input_input_line::hw_line(header, data.palette, false),
table::table(data, &state, (r, c), names, ie, eu),
hardware_input_input_line::hw_line(footer, data.palette, true),
)).bound_focus(BoundaryBehavior::Stop),
Page::Settings { header, footer } => VStack::new((
hardware_input_input_line::hw_line(header, data.palette, false),
settings::settings(data, &state, save_settings),
hardware_input_input_line::hw_line(footer, data.palette, true),
)).bound_focus(BoundaryBehavior::Wrap),
})
.background_color(data.palette.dark_blue(), Rectangle)
})
.focus_touches()
.map_event::<(), _>(|event: &Event, _state| match event {
Event::KeyDown(key) => match key {
Key::Character('h') | Key::LeftArrow => {
Some(FocusAction::Previous.into_event(focus::GROUP_1))
}
Key::Character('l') | Key::RightArrow => {
Some(FocusAction::Next.into_event(focus::GROUP_1))
}
Key::Character('k') | Key::UpArrow => {
Some(FocusAction::Previous.into_event(focus::GROUP_0))
}
Key::Character('j') | Key::DownArrow => {
Some(FocusAction::Next.into_event(focus::GROUP_0))
}
Key::Character(' ' | '\n') => Some(FocusAction::Select.into_event(focus::GROUP_0)),
Key::Character('e') | Key::Backspace => {
Some(FocusAction::Blur.into_event(focus::GROUP_0))
}
_ => None,
},
Event::KeyUp(_) => None,
_ => Some(event.clone()),
})
}
const PALETTE: definitions::Palette<Rgb888> = definitions::Palette::from_array([
Rgb888::new(0x00, 0x00, 0x00),
Rgb888::new(0x47, 0x47, 0xff),
Rgb888::new(0x00, 0x00, 0x80),
Rgb888::new(0x66, 0x66, 0x66),
Rgb888::new(0x00, 0xbc, 0x10),
Rgb888::new(0xd6, 0xd6, 0xd6),
Rgb888::new(0xe3, 0x87, 0x0e),
Rgb888::new(0xd1, 0x00, 0x00),
Rgb888::new(0xff, 0xff, 0xff),
Rgb888::new(0xe8, 0xf0, 0x00),
Rgb888::new(0x9b, 0x30, 0xff),
]);
fn main() {
let size = Size::new(320, 240);
let mut display: SimulatorDisplay<Rgb888> = SimulatorDisplay::new(size);
let mut target = EmbeddedGraphicsRenderTarget::new_hinted(&mut display, PALETTE.black());
let output_settings = OutputSettingsBuilder::new().scale(3).build();
let mut window = Window::new("Coffeeeee", &output_settings);
window.update(target.display());
let app_start = Instant::now();
let mut touch_tracker = MouseTracker::new();
let initial_state = State {
static_ip: core::net::Ipv4Addr::new(192, 168, 11, 100),
gateway: core::net::Ipv4Addr::new(192, 168, 11, 1),
dns: core::net::Ipv4Addr::new(192, 168, 11, 137),
dhcp: true,
net_mask: 24,
page: SETTINGS,
..Default::default()
};
let mut app =
App::new(initial_state, size.into(), root_view).with_roles(Role::Button | Role::Container);
app.focus_forward();
loop {
app.set_time(app_start.elapsed());
let events: Vec<_> = window
.events()
.filter_map(|event| {
if event == SimulatorEvent::Quit {
exit(0);
}
touch_tracker.process_event(event)
})
.collect();
for event in events {
app.send(event);
}
if let Some((i, ie)) = app.state().ie_value_update {
println!("IE value update: {i}: {ie}");
app.state_mut().ie_value_update = None;
}
if let Some(action) = app.state().page_action {
let current_page = app.state().page;
let new_page = match (action, current_page) {
(PageAction::Next, Page::IeTable { .. }) if current_page == PAGE_2 => SETTINGS,
(PageAction::Next, Page::IeTable { .. }) => PAGE_2,
(PageAction::Next, Page::Settings { .. }) => PAGE_1,
(PageAction::Prev, Page::Settings { .. }) => PAGE_2,
(PageAction::Prev, Page::IeTable { .. }) if current_page == PAGE_1 => SETTINGS,
(PageAction::Prev, Page::IeTable { .. }) => PAGE_1,
};
let mut state = app.state_mut();
state.page = new_page;
state.page_action = None;
}
if app.should_redraw() || target.clear_animation_status() {
app.render_animated(&mut target, &PALETTE.white());
if std::env::var("DEBUG_FOCUS").is_ok() {
app.draw_focus_overlay(&mut target, PALETTE.yellow(), 2);
}
window.update(target.display());
target.clear(PALETTE.black());
} else {
std::thread::sleep(Duration::from_millis(33));
}
}
}