use iced::font; use iced::time::{Duration, hours, minutes}; use iced::widget::{ center_x, center_y, column, container, row, scrollable, slider, table, text, tooltip, }; use iced::{Center, Element, Fill, Font, Right, Theme}; pub fn main() -> iced::Result { iced::application(Table::new, Table::update, Table::view) .theme(Theme::CatppuccinMocha) .run() } struct Table { events: Vec, padding: (f32, f32), separator: (f32, f32), } #[derive(Debug, Clone)] enum Message { PaddingChanged(f32, f32), SeparatorChanged(f32, f32), } impl Table { fn new() -> Self { Self { events: Event::list(), padding: (10.0, 5.0), separator: (1.0, 1.0), } } fn update(&mut self, message: Message) { match message { Message::PaddingChanged(x, y) => self.padding = (x, y), Message::SeparatorChanged(x, y) => self.separator = (x, y), } } fn view(&self) -> Element<'_, Message> { let table = { let bold = |header| { text(header).font(Font { weight: font::Weight::Bold, ..Font::DEFAULT }) }; let columns = [ table::column(bold("Name"), |event: &Event| text(&event.name)), table::column(bold("Time"), |event: &Event| { let minutes = event.duration.as_secs() / 60; text!("{minutes} min").style(if minutes > 90 { text::warning } else { text::default }) }) .align_x(Right) .align_y(Center), table::column(bold("Price"), |event: &Event| { if event.price > 0.0 { text!("${:.2}", event.price).style( if event.price > 100.0 { text::warning } else { text::default }, ) } else { text("Free").style(text::success).width(Fill).center() } }) .align_x(Right) .align_y(Center), table::column(bold("Rating"), |event: &Event| { text!("{:.2}", event.rating).style(if event.rating > 4.7 { text::success } else if event.rating < 2.0 { text::danger } else { text::default }) }) .align_x(Right) .align_y(Center), ]; table(columns, &self.events) .padding_x(self.padding.0) .padding_y(self.padding.1) .separator_x(self.separator.0) .separator_y(self.separator.1) }; let controls = { let labeled_slider = |label, range: std::ops::RangeInclusive, (x, y), on_change: fn(f32, f32) -> Message| { row![ text(label).font(Font::MONOSPACE).size(14).width(100), tooltip( slider(range.clone(), x, move |x| on_change(x, y)), text!("{x:.0}px").font(Font::MONOSPACE).size(10), tooltip::Position::Left ), tooltip( slider(range, y, move |y| on_change(x, y)), text!("{y:.0}px").font(Font::MONOSPACE).size(10), tooltip::Position::Right ), ] .spacing(10) .align_y(Center) }; column![ labeled_slider( "Padding", 0.0..=30.0, self.padding, Message::PaddingChanged ), labeled_slider( "Separator", 0.0..=5.0, self.separator, Message::SeparatorChanged ) ] .spacing(10) .width(400) }; column![ center_y(scrollable(center_x(table)).spacing(10)).padding(10), center_x(controls).padding(10).style(container::dark) ] .into() } } struct Event { name: String, duration: Duration, price: f32, rating: f32, } impl Event { fn list() -> Vec { vec![ Event { name: "Get lost in a hacker bookstore".to_owned(), duration: hours(2), price: 0.0, rating: 4.9, }, Event { name: "Buy vintage synth at Noisebridge flea market".to_owned(), duration: hours(1), price: 150.0, rating: 4.8, }, Event { name: "Eat a questionable hot dog at 2AM".to_owned(), duration: minutes(20), price: 5.0, rating: 1.7, }, Event { name: "Ride the MUNI for the story".to_owned(), duration: minutes(60), price: 3.0, rating: 4.1, }, Event { name: "Scream into the void from Twin Peaks".to_owned(), duration: minutes(40), price: 0.0, rating: 4.9, }, Event { name: "Buy overpriced coffee and feel things".to_owned(), duration: minutes(25), price: 6.5, rating: 4.5, }, Event { name: "Attend an underground robot poetry slam".to_owned(), duration: hours(1), price: 12.0, rating: 4.8, }, Event { name: "Browse cursed tech at a retro computer fair".to_owned(), duration: hours(2), price: 10.0, rating: 4.7, }, Event { name: "Try to order at a secret ramen place with no sign" .to_owned(), duration: minutes(50), price: 14.0, rating: 4.6, }, Event { name: "Join a spontaneous rooftop drone rave".to_owned(), duration: hours(3), price: 0.0, rating: 4.9, }, Event { name: "Sketch a stranger at Dolores Park".to_owned(), duration: minutes(45), price: 0.0, rating: 4.4, }, Event { name: "Visit the Museum of Obsolete APIs".to_owned(), duration: hours(1), price: 9.99, rating: 4.2, }, Event { name: "Chase the last working payphone".to_owned(), duration: minutes(35), price: 0.25, rating: 4.0, }, Event { name: "Trade zines with a punk on BART".to_owned(), duration: minutes(30), price: 3.5, rating: 4.7, }, Event { name: "Get a tattoo of the Git logo".to_owned(), duration: hours(1), price: 200.0, rating: 4.6, }, ] } }