diff --git a/Cargo.lock b/Cargo.lock index 7ce89be..09dfc12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1169,12 +1169,15 @@ version = "0.12.0" dependencies = [ "bitflags 1.3.2", "bytemuck", + "cosmic-text", "glam", "half", "iced_core", "log", "raw-window-handle", + "rustc-hash", "thiserror", + "twox-hash", ] [[package]] @@ -1237,8 +1240,6 @@ dependencies = [ "log", "once_cell", "raw-window-handle", - "rustc-hash", - "twox-hash", "wgpu", ] diff --git a/src/ui/basics.rs b/src/ui/basics.rs index 4b3ad0c..1f4a4b0 100644 --- a/src/ui/basics.rs +++ b/src/ui/basics.rs @@ -1,17 +1,19 @@ - #[derive(Copy, Clone, Debug)] +/// A cell within a [CellGrid] pub struct Cell { // display coordinates of the cell - pub x : f32, - pub y : f32, - pub width : f32, - pub height : f32, + pub x: f32, + pub y: f32, + pub width: f32, + pub height: f32, // position of the cell in the grid (0-based) pub pos_x: u32, pub pos_y: u32, } +/// A grid of equal cells, the grid has a position on the +/// display pub struct CellGrid { // display coordinates of the grid x: f32, @@ -22,6 +24,10 @@ pub struct CellGrid { // size of the grid, in number of cells num_x: u32, num_y: u32, +} + +pub struct CellGridCells<'a> { + grid: &'a CellGrid, // current position when iterating pos_x: u32, @@ -40,9 +46,22 @@ impl CellGrid { height, num_x: if num_y > 0 { num_x } else { 0 }, // if one dimension is 0, both shall be 0 num_y: if num_x > 0 { num_y } else { 0 }, // if one dimension is 0, both shall be 0 + } + } + + pub fn iter(&self) -> CellGridCells { + CellGridCells { + grid: self, pos_x: 0, pos_y: 0, - curr: Cell {x, y, width, height, pos_x: 0, pos_y: 0} + curr: Cell { + x: self.x, + y: self.y, + width: self.width, + height: self.height, + pos_x: 0, + pos_y: 0, + }, } } @@ -50,37 +69,48 @@ impl CellGrid { CellGrid::new(self.x, self.y, self.width, self.height, 1, self.num_y) } + pub fn compute_min_height(&self) -> f32 { + let mut min_height: f32 = self.height; + for cell in self.iter() { + min_height = min_height.min(cell.height) + } + min_height + } +} + +impl<'a> CellGridCells<'a> { fn compute_cell(&mut self) -> () { + let grid: &'a CellGrid = self.grid; self.curr.pos_x = self.pos_x; self.curr.pos_y = self.pos_y; - let beg_x: f32 = self.x + (self.width * self.curr.pos_x as f32) / self.num_x as f32; - let end_x: f32 = self.x + (self.width * (self.curr.pos_x + 1) as f32) / self.num_x as f32; + let beg_x: f32 = grid.x + (grid.width * self.curr.pos_x as f32) / grid.num_x as f32; + let end_x: f32 = grid.x + (grid.width * (self.curr.pos_x + 1) as f32) / grid.num_x as f32; self.curr.x = beg_x.trunc(); self.curr.width = end_x.trunc() - self.curr.x; - let beg_y: f32 = self.y + (self.height * self.curr.pos_y as f32) / self.num_y as f32; - let end_y: f32 = self.y + (self.height * (self.curr.pos_y + 1) as f32) / self.num_y as f32; + let beg_y: f32 = grid.y + (grid.height * self.curr.pos_y as f32) / grid.num_y as f32; + let end_y: f32 = grid.y + (grid.height * (self.curr.pos_y + 1) as f32) / grid.num_y as f32; self.curr.y = beg_y.trunc(); self.curr.height = end_y.trunc() - self.curr.y; } } -impl Iterator for CellGrid { +impl<'a> Iterator for CellGridCells<'a> { type Item = Cell; fn next(&mut self) -> Option { - if self.pos_y >= self.num_y { + if self.pos_y >= self.grid.num_y { None } else { self.compute_cell(); self.pos_x += 1; - if self.pos_x >= self.num_x { + if self.pos_x >= self.grid.num_x { self.pos_x = 0; self.pos_y += 1; } Some(self.curr) } } -} \ No newline at end of file +} diff --git a/src/ui/calendar.rs b/src/ui/calendar.rs index 88bdae4..c207b35 100644 --- a/src/ui/calendar.rs +++ b/src/ui/calendar.rs @@ -8,14 +8,14 @@ // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. -use iced::advanced::{layout, renderer}; -use iced::advanced::widget::{Tree, Widget}; -use iced::{Color, Element, Length, Rectangle, alignment}; -use iced::advanced::text::{self, Text, LineHeight, Shaping}; -use chrono::{NaiveDate, Datelike, Duration, Local, Months}; use super::basics::CellGrid; -use crate::model::events::{EventsCollection, Event}; +use crate::model::events::{Event, EventsCollection}; +use chrono::{Datelike, Duration, Local, Months, NaiveDate}; +use iced::advanced::text::{self, LineHeight, Shaping, Text, Paragraph}; +use iced::advanced::widget::{Tree, Widget}; +use iced::advanced::{layout, renderer}; use iced::mouse; +use iced::{alignment, Color, Element, Length, Pixels, Point, Rectangle, Size}; #[cfg(feature = "tracing")] extern crate lttng_ust; @@ -24,27 +24,13 @@ extern crate lttng_ust; use lttng_ust::import_tracepoints; #[cfg(feature = "tracing")] -import_tracepoints!( - concat!(env!("OUT_DIR"), "/tracepoints.rs"), - tracepoints -); +import_tracepoints!(concat!(env!("OUT_DIR"), "/tracepoints.rs"), tracepoints); -const MONTH_NAMES: [&str;12] = [ - "gen", - "feb", - "mar", - "apr", - "mag", - "giu", - "lug", - "ago", - "set", - "ott", - "nov", - "dic", +const MONTH_NAMES: [&str; 12] = [ + "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic", ]; -const DAY_NAMES: [&str;7] = ["LUN", "MAR", "MER", "GIO", "VEN", "SAB", "DOM"]; +const DAY_NAMES: [&str; 7] = ["LUN", "MAR", "MER", "GIO", "VEN", "SAB", "DOM"]; //------------------------------------------------------------------------- @@ -61,7 +47,7 @@ pub struct CalendarParams { ev_height: f32, ev_bg: Color, - ev_fontsize: f32 + ev_fontsize: f32, } impl CalendarParams { @@ -77,7 +63,7 @@ impl CalendarParams { day_text_margin: 5.0, ev_height: 20.0, ev_bg: Color::from_rgb8(200, 245, 200), - ev_fontsize: 16.0 + ev_fontsize: 16.0, } } } @@ -90,13 +76,13 @@ fn render_events_in_row( first_day: NaiveDate, num_days: i64, row_bounds: Rectangle, - font_size: f32, + font_size: Pixels, fg: Color, content: &str, - events: &EventsCollection + events: &EventsCollection, ) { if num_days < 1 { - return + return; } #[cfg(feature = "tracing")] tracepoints::calendar::draw_days_entry(row_bounds.width as i32, row_bounds.height as i32); @@ -106,15 +92,19 @@ fn render_events_in_row( ev: &'a Event, bounds: Rectangle, } - // render events, if enough space - let day_text_height = renderer.measure( + let paragraph = renderer.create_paragraph(Text { content, - font_size, - LineHeight::default(), - renderer.default_font(), - row_bounds.size(), - Shaping::default()).height; + bounds: row_bounds.size(), + size: font_size, + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }); + let day_text_height = paragraph.min_height(); + // render events, if enough space let last_day = first_day + Duration::days(num_days - 1); let all_events = events.within(first_day, last_day); let x = row_bounds.x; @@ -123,18 +113,43 @@ fn render_events_in_row( let mut ev_y: f32 = 0.0; - let mut ev_bars : Vec = all_events.iter().map(|e| EventBar{ev: e, bounds: Rectangle {x, y, width: 0.0, height: ev_height}}).collect(); + let mut ev_bars: Vec = all_events + .iter() + .map(|e| EventBar { + ev: e, + bounds: Rectangle { + x, + y, + width: 0.0, + height: ev_height, + }, + }) + .collect(); // TODO: incompatible types num_days, grid num_cols - let row_grid = CellGrid::new(row_bounds.x, row_bounds.y, row_bounds.width, row_bounds.height, num_days.try_into().unwrap(), 1); + let row_grid = CellGrid::new( + row_bounds.x, + row_bounds.y, + row_bounds.width, + row_bounds.height, + num_days.try_into().unwrap(), + 1, + ); let mut current_day = first_day; + // use the minimum row height to compute available space for event bars + // to avoid inconsistentencies when rowas have slightly different heights + // and some can fit more event bars than others + let min_row_height = row_grid.compute_min_height(); + // update event bars - for cell in row_grid { + for cell in row_grid.iter() { ev_y = y; for ev_bar in ev_bars.iter_mut() { - if ev_bar.ev.begin == current_day || (ev_bar.ev.begin < first_day && current_day == first_day) { + if ev_bar.ev.begin == current_day + || (ev_bar.ev.begin < first_day && current_day == first_day) + { // start of event ev_bar.bounds.x = cell.x; ev_bar.bounds.y = ev_y; @@ -152,28 +167,33 @@ fn render_events_in_row( for ev_bar in &mut ev_bars { // close events that exceed the row - if ev_bar.ev.end >= current_day { + if ev_bar.ev.end >= current_day { ev_bar.bounds.width = row_bounds.x + row_bounds.width - ev_bar.bounds.x; } - if row_bounds.y + row_bounds.height > ev_bar.bounds.y + ev_bar.bounds.height { - renderer.fill_quad(renderer::Quad { + if row_bounds.y + min_row_height > ev_bar.bounds.y + ev_bar.bounds.height { + renderer.fill_quad( + renderer::Quad { bounds: ev_bar.bounds, border_radius: 0.0.into(), border_width: 1.0, border_color: params.day_other_month_fg, }, - params.ev_bg); - renderer.fill_text(Text { - content: ev_bar.ev.text.as_str(), - bounds: ev_bar.bounds, - size: params.ev_fontsize, - line_height: LineHeight::default(), - color: fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - shaping: Shaping::default(), - }); + params.ev_bg, + ); + renderer.fill_text( + Text { + content: ev_bar.ev.text.as_str(), + bounds: ev_bar.bounds.size(), + size: params.ev_fontsize.into(), + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }, + ev_bar.bounds.position(), + fg, + ); ev_y += ev_height } } @@ -205,7 +225,8 @@ impl<'a> CalendarMonthView<'a> { let weekday_on_first = first_day.weekday(); // first visible day in the view - let first_day_in_view = first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); + let first_day_in_view = + first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); Self { first_day, @@ -229,27 +250,25 @@ impl<'a> CalendarMonthView<'a> { let weekday_on_first = first_day.weekday(); // first visible day in the view - let first_day_in_view = first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); + let first_day_in_view = + first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); self.first_day = first_day; self.first_day_in_view = first_day_in_view; } - fn draw_header( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - week_w: f32, - ) { + fn draw_header(&self, renderer: &mut impl text::Renderer, bounds: Rectangle, week_w: f32) { // paint background over full width if self.params.header_bg != Color::TRANSPARENT { - renderer.fill_quad(renderer::Quad { + renderer.fill_quad( + renderer::Quad { bounds, border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, - self.params.header_bg); + self.params.header_bg, + ); } // redefine bounds to skip the week column @@ -257,100 +276,106 @@ impl<'a> CalendarMonthView<'a> { x: bounds.x + week_w, y: bounds.y, width: bounds.width - week_w, - height: bounds.height}; + height: bounds.height, + }; // font dimension - let font_size = renderer.default_size() as f32; + let font_size = renderer.default_size(); let h_axis = CellGrid::new(bounds.x, bounds.y, bounds.width, bounds.height, 7, 1); - for cell in h_axis { + for cell in h_axis.iter() { let bounds = Rectangle { x: cell.x + self.params.day_text_margin, y: bounds.center_y(), width: cell.width, - height: bounds.height + height: bounds.height, }; // label (day letter on row 0, day number on the rest) - let t = DAY_NAMES[cell.pos_x as usize]; + let t = DAY_NAMES[cell.pos_x as usize]; // color of text let fg = self.params.header_fg; - renderer.fill_text(Text { - content : t, - bounds, - size: font_size, - line_height: LineHeight::default(), - color: fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::default(), - }); + renderer.fill_text( + Text { + content: t, + bounds: bounds.size(), + size: font_size, + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + shaping: Shaping::default(), + }, + bounds.position(), + fg, + ); } } - fn draw_week_column( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - ) { + fn draw_week_column(&self, renderer: &mut impl text::Renderer, bounds: Rectangle) { let mut day = self.first_day; - let v_axis = CellGrid::new(bounds.x, bounds.y, self.week_column_width, bounds.height, 1, 6); - for cell in v_axis { + let v_axis = CellGrid::new( + bounds.x, + bounds.y, + self.week_column_width, + bounds.height, + 1, + 6, + ); + for cell in v_axis.iter() { // where to place the week number let day_bounds = Rectangle { x: bounds.x, y: cell.y + self.params.day_text_margin, width: cell.width, - height: cell.height + height: cell.height, }; let week_of_first_day_of_month = day.iso_week().week(); day += Duration::weeks(1); // render week cell text - renderer.fill_text(Text { - content : &(week_of_first_day_of_month).to_string(), - bounds: Rectangle { - x: day_bounds.center_x(), - y: day_bounds.y, - ..day_bounds - }, - size: self.week_column_font_size, - line_height: LineHeight::default(), - color: self.params.day_fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Center, - vertical_alignment: alignment::Vertical::Top, - shaping: Shaping::default(), - }); + renderer.fill_text( + Text { + content: &(week_of_first_day_of_month).to_string(), + bounds: day_bounds.size(), + size: self.week_column_font_size.into(), + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Center, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }, + Point { + x: day_bounds.center_x(), + y: day_bounds.y, + }, + self.params.day_fg, + ); } } - fn draw_days( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - ) { + fn draw_days(&self, renderer: &mut impl text::Renderer, bounds: Rectangle) { // font dimension - let font_size = renderer.default_size() as f32; + let font_size = renderer.default_size(); let mut current_day = self.first_day_in_view; let grid = CellGrid::new(bounds.x, bounds.y, bounds.width, bounds.height, 7, 6); - for row in grid.rows() { + let min_row_height = grid.compute_min_height(); + for row in grid.rows().iter() { let row_first_day = current_day; let row_grid = CellGrid::new(row.x, row.y, row.width, row.height, 7, 1); - for cell in row_grid { + for cell in row_grid.iter() { let day_bounds = Rectangle { x: cell.x, y: cell.y, width: cell.width, - height : cell.height + height: cell.height, }; // label (day letter on row 0, day number on the rest) @@ -373,30 +398,35 @@ impl<'a> CalendarMonthView<'a> { Color::TRANSPARENT }; - // where to place the day content - let x = day_bounds.x + self.params.day_text_margin; - let y = day_bounds.y + self.params.day_text_margin; - - renderer.fill_quad(renderer::Quad { - bounds: Rectangle {width: day_bounds.width + 0.5, height: day_bounds.height + 0.5, ..day_bounds}, + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + width: day_bounds.width + 0.5, + height: day_bounds.height + 0.5, + ..day_bounds + }, border_radius: 0.0.into(), border_width: 1.0, border_color: self.params.day_other_month_fg, }, - bg_color); + bg_color, + ); // render day cell text - renderer.fill_text(Text { - content, - bounds: Rectangle {x, y, ..day_bounds}, - size: font_size, - line_height: LineHeight::default(), - color: fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - shaping: Shaping::default() - }); + renderer.fill_text( + Text { + content, + bounds: day_bounds.size(), + size: font_size, + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }, + bounds.position(), + fg, + ); current_day = current_day.succ_opt().unwrap(); } @@ -404,7 +434,7 @@ impl<'a> CalendarMonthView<'a> { x: row.x, y: row.y, width: row.width, - height : row.height + height: row.height, }; let content = "10"; render_events_in_row( @@ -416,11 +446,10 @@ impl<'a> CalendarMonthView<'a> { font_size, self.params.day_fg, content, - self.events + self.events, ); } } - } // CalendarMonthView impl Widget for CalendarMonthView<'_> @@ -437,6 +466,7 @@ where fn layout( &self, + _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { @@ -464,11 +494,18 @@ where }; // font and header dimension - let font_size = renderer.default_size() as f32; + let font_size = f32::from(renderer.default_size()); let first_row_h = font_size + margin; // header - self.draw_header(renderer, Rectangle {height: first_row_h, ..bounds}, week_w); + self.draw_header( + renderer, + Rectangle { + height: first_row_h, + ..bounds + }, + week_w, + ); // week column if week_w > 0.0 { @@ -476,7 +513,15 @@ where let y = bounds.y + first_row_h; let width = self.week_column_width; let height = bounds.height - first_row_h; - self.draw_week_column(renderer, Rectangle{x, y, width, height}); + self.draw_week_column( + renderer, + Rectangle { + x, + y, + width, + height, + }, + ); } // monthly calendar cells @@ -484,7 +529,15 @@ where let y = bounds.y + first_row_h; let width = bounds.width - week_w; let height = bounds.height - first_row_h; - self.draw_days(renderer, Rectangle{x, y, width, height}); + self.draw_days( + renderer, + Rectangle { + x, + y, + width, + height, + }, + ); } } @@ -499,6 +552,9 @@ where //------------------------------------------------------------------------- +// 5 weeks plus two extra days is enough to accomodate the longest months of 31 days +static YEAR_VIEW_DAYS_PER_ROW: u32 = 5 * 7 + 2; + pub struct CalendarYearView<'a> { first_day: NaiveDate, first_day_in_view: NaiveDate, @@ -517,7 +573,8 @@ impl<'a> CalendarYearView<'a> { let weekday_on_first = first_day.weekday(); // first visible day in the view - let first_day_in_view = first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); + let first_day_in_view = + first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); Self { first_day, @@ -525,7 +582,7 @@ impl<'a> CalendarYearView<'a> { params: params.clone(), month_column_font_size: 24.0, margin: 10.0, - events + events, } } @@ -537,24 +594,22 @@ impl<'a> CalendarYearView<'a> { let weekday_on_first = self.first_day.weekday(); // first visible day in the view - self.first_day_in_view = self.first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); + self.first_day_in_view = + self.first_day - Duration::days(weekday_on_first.num_days_from_monday() as i64); } - fn draw_header( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - week_w: f32, - ) { + fn draw_header(&self, renderer: &mut impl text::Renderer, bounds: Rectangle, week_w: f32) { // paint background over full width if self.params.header_bg != Color::TRANSPARENT { - renderer.fill_quad(renderer::Quad { + renderer.fill_quad( + renderer::Quad { bounds, border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, - self.params.header_bg); + self.params.header_bg, + ); } // redefine bounds to skip the week column @@ -562,20 +617,28 @@ impl<'a> CalendarYearView<'a> { x: bounds.x + week_w, y: bounds.y, width: bounds.width - week_w, - height: bounds.height}; + height: bounds.height, + }; // font dimension - let font_size = renderer.default_size() as f32; + let font_size = renderer.default_size(); let days_of_week = ["L", "M", "M", "G", "V", "S", "D"]; - let grid = CellGrid::new(bounds.x, bounds.y, bounds.width, bounds.height, 42, 1); - for cell in grid { + let grid = CellGrid::new( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + YEAR_VIEW_DAYS_PER_ROW, + 1, + ); + for cell in grid.iter() { let bounds = Rectangle { x: cell.x + 0.5, y: cell.y + 0.5, width: cell.width, - height: cell.height + height: cell.height, }; let weekday = (cell.pos_x as usize) % 7; @@ -587,41 +650,42 @@ impl<'a> CalendarYearView<'a> { Color::TRANSPARENT }; - renderer.fill_quad(renderer::Quad { + renderer.fill_quad( + renderer::Quad { bounds, border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, - bg_color); + bg_color, + ); // label (day letter on row 0, day number on the rest) - let t = days_of_week[weekday]; + let t = days_of_week[weekday]; // color of text let fg = self.params.header_fg; let x = bounds.x + self.params.day_text_margin; let y = bounds.center_y(); - renderer.fill_text(Text { - content : t, - bounds: Rectangle {x, y, ..bounds}, - size: font_size, - line_height: LineHeight::default(), - color: fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::default(), - }); + renderer.fill_text( + Text { + content: t, + bounds: bounds.size(), + size: font_size, + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + shaping: Shaping::default(), + }, + Point { x, y }, + fg, + ); } } - fn draw_month_column( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - ) { + fn draw_month_column(&self, renderer: &mut impl text::Renderer, bounds: Rectangle) { // dimensions of each box representing a month name let h: f32 = bounds.height / 12.0; @@ -631,57 +695,79 @@ impl<'a> CalendarYearView<'a> { x: bounds.x, y: (month as f32) * h + bounds.y + self.params.day_text_margin, width: bounds.width, - height: h + height: h, }; // render month name - renderer.fill_text(Text { - content : MONTH_NAMES[month], - bounds: Rectangle { - x: month_name_bounds.x + self.margin, - y: month_name_bounds.center_y(), - ..month_name_bounds - }, - size: self.month_column_font_size, - line_height: LineHeight::default(), - color: self.params.day_fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - shaping: Shaping::default(), - }); + renderer.fill_text( + Text { + content: MONTH_NAMES[month], + bounds: month_name_bounds.size(), + size: self.month_column_font_size.into(), + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + shaping: Shaping::default(), + }, + Point { + x: month_name_bounds.x + self.margin, + y: month_name_bounds.center_y(), + }, + self.params.day_fg, + ); } } - fn draw_days( - &self, - renderer: &mut impl text::Renderer, - bounds: Rectangle, - ) { + fn draw_days(&self, renderer: &mut impl text::Renderer, bounds: Rectangle) { // font dimension - let font_size = renderer.default_size() as f32; + let font_size = renderer.default_size(); - let grid = CellGrid::new(bounds.x, bounds.y, bounds.width, bounds.height, 42, 12); + let grid = CellGrid::new( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + YEAR_VIEW_DAYS_PER_ROW, + 12, + ); - for row in grid.rows() { - let row_grid = CellGrid::new(row.x, row.y, row.width, row.height, 42, 1); + for row in grid.rows().iter() { + let row_grid = CellGrid::new( + row.x, + row.y, + row.width, + row.height, + YEAR_VIEW_DAYS_PER_ROW, + 1, + ); // the row index is the month let month = row.pos_y; - let first_day_of_month = self.first_day.with_day0(0).unwrap().with_month0(month).unwrap(); + let first_day_of_month = self + .first_day + .with_day0(0) + .unwrap() + .with_month0(month) + .unwrap(); let first_weekday = first_day_of_month.weekday().num_days_from_monday(); - let mut row_bounds: Rectangle = Rectangle{x: row.x, y: row.y, width: row.width, height: row.height}; + let mut row_bounds: Rectangle = Rectangle { + x: row.x, + y: row.y, + width: row.width, + height: row.height, + }; let row_days = ((first_day_of_month + Months::new(1)) - first_day_of_month).num_days(); - for cell in row_grid { - + for cell in row_grid.iter() { let day_bounds = Rectangle { x: cell.x, y: cell.y, width: cell.width, - height : cell.height + height: cell.height, }; - let current_day = first_day_of_month + Duration::days((cell.pos_x as i64) - (first_weekday as i64)); + let current_day = first_day_of_month + + Duration::days((cell.pos_x as i64) - (first_weekday as i64)); if current_day.month0() == month { if current_day.day() == 1 { @@ -709,64 +795,68 @@ impl<'a> CalendarYearView<'a> { Color::TRANSPARENT }; - // where to place the day content - let x = day_bounds.x + self.params.day_text_margin; - let y = day_bounds.y + self.params.day_text_margin; - - renderer.fill_quad(renderer::Quad { + renderer.fill_quad( + renderer::Quad { bounds: day_bounds, border_radius: 0.0.into(), border_width: 1.0, border_color: self.params.day_other_month_fg, }, - bg_color); + bg_color, + ); // render day cell text - renderer.fill_text(Text { - content, - bounds: Rectangle {x, y, ..day_bounds}, - size: font_size, - line_height: LineHeight::default(), - color: fg, - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - shaping: Shaping::default(), - }); - - } + renderer.fill_text( + Text { + content, + bounds: day_bounds.size(), + size: font_size, + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }, + day_bounds.position(), + fg, + ); } -// let num_days = grid.num_cols() as i64; // ((current_day + Months::new(1)) - current_day).num_days(); - let content = "10"; - render_events_in_row( - &self.params, - renderer, - first_day_of_month, - row_days as i64, - row_bounds, - font_size, - self.params.day_fg, - content, - self.events - ); } + let content = "10"; + render_events_in_row( + &self.params, + renderer, + first_day_of_month, + row_days as i64, + row_bounds, + font_size, + self.params.day_fg, + content, + self.events, + ); + } } - fn compute_month_col_width(&self, renderer: &mut impl text::Renderer) -> f32 { + fn compute_month_col_width(&self, renderer: &mut impl text::Renderer, bounds: Size) -> f32 { let mut max_max_font_width = 0.0; for month_name in MONTH_NAMES { - let month_width = renderer.measure_width( - month_name, - self.month_column_font_size, - renderer.default_font(), - Shaping::default()); + let paragraph = renderer.create_paragraph(Text { + content: month_name, + bounds, + size: renderer.default_size(), + line_height: LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: Shaping::default(), + }); + let month_width = paragraph.min_width(); if month_width > max_max_font_width { max_max_font_width = month_width; } } - return max_max_font_width; + return max_max_font_width + self.margin; } - } // CalendarYearView impl Widget for CalendarYearView<'_> @@ -783,6 +873,7 @@ where fn layout( &self, + _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { @@ -807,17 +898,24 @@ where // week column only visible if there is enough space let month_w = if self.params.show_weeks { //self.month_column_width - self.compute_month_col_width(renderer) + self.margin + self.compute_month_col_width(renderer, bounds.size()) + self.margin } else { 0.0 }; // font and header dimension - let font_size = renderer.default_size() as f32; + let font_size = f32::from(renderer.default_size()); let first_row_h = font_size + margin; // header - self.draw_header(renderer, Rectangle {height: first_row_h, ..bounds}, month_w); + self.draw_header( + renderer, + Rectangle { + height: first_row_h, + ..bounds + }, + month_w, + ); // month column if month_w > 0.0 && bounds.width > month_w { @@ -825,7 +923,15 @@ where let y = bounds.y + first_row_h; let width = month_w; let height = bounds.height - first_row_h; - self.draw_month_column(renderer, Rectangle{x, y, width, height}); + self.draw_month_column( + renderer, + Rectangle { + x, + y, + width, + height, + }, + ); } // monthly calendar cells @@ -833,7 +939,15 @@ where let y = bounds.y + first_row_h; let width = bounds.width - month_w; let height = bounds.height - first_row_h; - self.draw_days(renderer, Rectangle{x, y, width, height}); + self.draw_days( + renderer, + Rectangle { + x, + y, + width, + height, + }, + ); #[cfg(feature = "tracing")] tracepoints::calendar::draw_exit(bounds.width as i32, bounds.height as i32); }