add error report view
[?]
Sep 10, 2025, 7:30 PM
CULHFNIVQ3ATML2W3Z45RARZ2LHGXONYTGGN2ETWAAMV7R3Y67AQCDependencies
- [2]
6YZAVBWUInitial commit - [3]
AMPZ2BXKshow changed files diffs (only Edit atm) - [4]
23SFYK4Qbig view refactor into a new crate - [5]
OPXFZKEBview tests setup - [6]
3QVNMRNMtest non-empty repo app view - [7]
MYGIBRRHwip custom theme - [8]
PKJCFSBMtheme improvements - [9]
XSZZB47Urefactor stuff into lib - [10]
XIASAP3Gclippy - [11]
3BK22XE5add a test for hover btn and more refactors - [12]
ZD56BUSUadd back +/- bg colors - [13]
I2AG42PAnew cols layout - [14]
SASAN2XCuse nav-scrollable - [15]
UR4J677Rnav for log changes and refactors - [16]
A6Z4O6RCactions menu - [17]
JZXYSIYDchannel selection! - [18]
5ZRDYL6Kfork channel, fix recording esc key - [19]
BAUK5BONpimp-up action buttons - [20]
FJSVMFB4add `iced_expl_widgets` with forked scrollable - [21]
3XRG4BB6rewritten nav-scrollable! - [22]
WAOGSCOJvery nice refactor, wip adding channels logs - [23]
WH57EHNMupdate tests - [24]
EJPSD5XOshared allowed actions conditions between update and view - [25]
YK3MOJJLchonky refactor, wip other channels logs & diffs - [26]
7WCB5YQJrefactor msgs and modules - [27]
AZ5D2LQUallow to set record description - [28]
PKLUHYE4allow to copy change hash - [29]
YKHE3XMWrefactor diffs handling - [30]
ZIUHKVJKupdate tests - [31]
KEPKF3WOunify diffs handling, simplify view - [32]
PTWZYQFRuse nav-scrollable for repo status
Change contents
- replacement in inflorescence_view/src/view.rs at line 7
use iced_expl_widget::nav_scrollable;use iced_expl_widget::{nav_scrollable, report}; - replacement in inflorescence_view/src/view.rs at line 16
use iced::{alignment, font, window, Element, Font, Length, Padding};use iced::{alignment, font, window, Alignment, Element, Font, Length, Padding,}; - edit in inflorescence_view/src/view.rs at line 51
report, - edit in inflorescence_view/src/view.rs at line 54
// TODO: show report in load and other states - edit in inflorescence_view/src/view.rs at line 69
report, - edit in inflorescence_view/src/view.rs at line 86
report: &'a report::Container, - edit in inflorescence_view/src/view.rs at line 946
// Overlay the main view with reportslet main = report::view(report, main, |report| {let report_width =if window_size.width > DEFAULT_MIN_COL_WIDTH as f32 * 1.3 {window_size.width * 0.45} else {window_size.width * 0.95};let max_height = window_size.height * 0.85;el(container(column([el(row([el(container(button(text("↧").shaping(text::Shaping::Advanced)).on_press(Msg::Action(action::FilteredMsg::ToggleErrorReports),),).width(Length::Fill).align_x(Alignment::End))]).width(Length::Fill)),el(container(column(report.entries.iter().map(view_report_entry))).padding([4, 6]).class(theme::Container::Report),),])).width(Length::Fixed(report_width)).height(Length::Shrink).max_height(max_height))}); - edit in inflorescence_view/src/view.rs at line 979
}fn view_report_entry<'a>(entry: &'a report::Entry) -> Element<'a, Msg, Theme>whereMsg: 'a,{let report::Entry {level,msg,time,is_read,} = entry;let time = time.strftime("%I:%M:%S.%3f").to_string();let text_class = if *is_read {theme::Text::SlightlyFaded} else {theme::Text::Normal};el(row([el(text(msg).width(Length::Fill).wrapping(text::Wrapping::WordOrGlyph).class(text_class)),el(container(row([])).width(Length::Fixed(8.0)).height(Length::Fill).class(theme::Container::ReportLevel {level: *level,is_read: *is_read,})),el(text(time).align_x(Alignment::End).size(11.0).class(text_class)),]).spacing(6).padding(4).width(Length::Fill).height(Length::Shrink)) - edit in inflorescence_view/src/view/test.rs at line 6
use iced_expl_widget::report; - edit in inflorescence_view/src/view/test.rs at line 17
use std::str::FromStr; - replacement in inflorescence_view/src/view/test.rs at line 36
let uniq_name = "app_loading_repo";let uniq_name = "loading_repo"; - edit in inflorescence_view/src/view/test.rs at line 47
report: report::Container::default(), - replacement in inflorescence_view/src/view/test.rs at line 54
let uniq_name = "app_loaded_empty_repo";let uniq_name = "loaded_empty_repo"; - edit in inflorescence_view/src/view/test.rs at line 78
report: report::Container::default(), - replacement in inflorescence_view/src/view/test.rs at line 85
let uniq_name = "app_loaded_non_empty_repo";let uniq_name = "loaded_non_empty_repo"; - edit in inflorescence_view/src/view/test.rs at line 123
report: report::Container::default(), - replacement in inflorescence_view/src/view/test.rs at line 130
let uniq_name = "app_loaded_point_at_untracked";let uniq_name = "loaded_point_at_untracked"; - replacement in inflorescence_view/src/view/test.rs at line 132
test_view_change_sim(&mut results, uniq_name, view(&state), size, |sim| {test_view_change_sim(mresults, uniq_name, view(&state), size, |sim| { - replacement in inflorescence_view/src/view/test.rs at line 139
let uniq_name = "app_loaded_selected_untracked";let uniq_name = "loaded_selected_untracked"; - edit in inflorescence_view/src/view/test.rs at line 163
report: report::Container::default(), - replacement in inflorescence_view/src/view/test.rs at line 165
test_view(&mut results, uniq_name, view(&state), size);test_view(mresults, uniq_name, view(&state), size);// _________________________________________________________________________//let uniq_name = "loaded_shown_error_report";let repo = repo::State {dir_name: "path".to_string(),channel: "some_channel".to_string(),other_channels: vec![],untracked_files: BTreeSet::new(),changed_files: BTreeMap::new(),short_log: vec![],};let ready_state = ReadyState {user_id: Id::default().unwrap(),repo,selection: default(),navigation: default(),record_changes: default(),logs: default(),forking_channel_name: default(),};let repo_path = PathBuf::from("test/repo/path");let state = State {window_size: WINDOW_SIZE,repo_path: repo_path.clone(),sub: SubState::Ready(ready_state),allowed_actions: vec![],report: report::Container {hidden: false,entries: vec![report::Entry {level: report::Level::Error,msg: "Sed a rhoncus elit. Vestibulum molestie lacus blandit eleifend auctor. Nulla eget fermentum erat. Suspendisse quis erat faucibus enim facilisis cursus.".to_string(),time: Timestamp::from_str("2024-03-10T06:05:00Z").unwrap(),is_read: false,},report::Entry {level: report::Level::Warning,msg: "Morbi at justo".to_string(),time: Timestamp::from_str("2024-03-10T06:04:33Z").unwrap(),is_read: false,},report::Entry {level: report::Level::Error,msg: "Curabitur sit amet nisl venenatis, suscipit magna at, elementum dolor".to_string(),time: Timestamp::from_str("2024-03-10T06:02:10Z").unwrap(),is_read: true,},report::Entry {level: report::Level::Warning,msg: "Aliquam lobortis egestas diam, a luctus dui imperdiet vitae".to_string(),time: Timestamp::from_str("2024-03-10T06:00:00Z").unwrap(),is_read: true,},],},}; - edit in inflorescence_view/src/view/test.rs at line 225
test_view(mresults, uniq_name, view(&state), size); - replacement in inflorescence_view/src/theme.rs at line 4
use iced_expl_widget::nav_scrollable;use iced_expl_widget::{nav_scrollable, report}; - edit in inflorescence_view/src/theme.rs at line 36
const TURQOISE_DARKER: Color = color!(0x0CA592); - edit in inflorescence_view/src/theme.rs at line 39
const TEXT_COLOR_FADED: Color = Color {a: 0.7,..TEXT_COLOR}; - edit in inflorescence_view/src/theme.rs at line 46
const REPORT_ERROR_COLOR: Color = color!(0x9d140e);const REPORT_WARNING_COLOR: Color = color!(0x999208); - edit in inflorescence_view/src/theme.rs at line 98
Report,ReportLevel { level: report::Level, is_read: bool }, - edit in inflorescence_view/src/theme.rs at line 111
SlightlyFaded, - edit in inflorescence_view/src/theme.rs at line 225
..default()},Container::Report => container::Style {background: Some(ACTIONS_BG),border: Border {color: TURQOISE_DARKER,width: 1.0,..default()}, - edit in inflorescence_view/src/theme.rs at line 236
Container::ReportLevel { level, is_read } => {let color = match level {report::Level::Warning => REPORT_WARNING_COLOR,report::Level::Error => REPORT_ERROR_COLOR,};let color = if *is_read {Color { a: 0.7, ..color }} else {color};container::Style {background: Some(Background::Color(color)),..default()}} - edit in inflorescence_view/src/theme.rs at line 345
Text::SlightlyFaded => text::Style {color: Some(TEXT_COLOR_FADED),}, - replacement in inflorescence_model/src/model.rs at line 3
use iced_expl_widget::nav_scrollable;use iced_expl_widget::{nav_scrollable, report}; - edit in inflorescence_model/src/model.rs at line 33
pub report: report::Container, - replacement in inflorescence_model/src/action.rs at line 3
use iced_expl_widget::nav_scrollable;use iced_expl_widget::{nav_scrollable, report}; - edit in inflorescence_model/src/action.rs at line 34
ToggleErrorReports, - edit in inflorescence_model/src/action.rs at line 73
(ToggleErrorReports, ToggleErrorReports) => true, - edit in inflorescence_model/src/action.rs at line 90
(ToggleErrorReports, _) => false, - replacement in inflorescence_model/src/action.rs at line 94
pub fn get_allowed(state: &model::SubState) -> Vec<Binding> {match state {pub fn get_allowed(state: &model::SubState,report: &report::Container,) -> Vec<Binding> {let mut bindings = match state { - edit in inflorescence_model/src/action.rs at line 102
};if !report.entries.is_empty() {let label = if !report.hidden {"Hide errors"} else {"Show errors"};bindings.push(Binding {key: "C-e",label,msg: Some(FilteredMsg::ToggleErrorReports),}); - edit in inflorescence_model/src/action.rs at line 116
bindings - edit in inflorescence/src/main.rs at line 8
use iced_expl_widget::report; - edit in inflorescence/src/main.rs at line 120
report: report::Container::default(), - replacement in inflorescence/src/main.rs at line 185
state.model.allowed_actions = action::get_allowed(&state.model.sub);state.model.allowed_actions =action::get_allowed(&state.model.sub, &state.model.report); - edit in inflorescence/src/main.rs at line 485
action::FilteredMsg::ToggleErrorReports => {state.model.report.hidden = !state.model.report.hidden;Task::none()} - edit in inflorescence/src/main.rs at line 1413
"e" if mods == Modifiers::CTRL => {action(action::FilteredMsg::ToggleErrorReports)} - file addition: report.rs[20.713]
//! Widget used to display timestamped error and warning reports in an overlay.use iced::advanced::{layout, overlay};use iced::{mouse, Alignment, Element, Event, Point, Rectangle, Size, Vector};use iced_core::widget::{Operation, Tree};use iced_core::{renderer, Clipboard, Layout, Shell};use jiff::Timestamp;#[derive(Clone, Copy, Debug)]pub enum Level {Warning,Error,}#[derive(Debug)]pub struct Entry {pub level: Level,pub msg: String,pub time: Timestamp,pub is_read: bool,}#[derive(Debug, Default)]pub struct Container {pub hidden: bool,pub entries: Vec<Entry>,}struct Widget<'a, Msg, Theme, Renderer> {pub overlay: Element<'a, Msg, Theme, Renderer>,pub overlaid: Element<'a, Msg, Theme, Renderer>,}pub fn view<'a, Msg, Theme, Renderer, FV>(container: &'a Container,overlaid: Element<'a, Msg, Theme, Renderer>,view_container: FV,) -> Element<'a, Msg, Theme, Renderer>whereMsg: 'a,Theme: 'a,Renderer: 'a + iced::advanced::Renderer,FV: Fn(&'a Container) -> Element<'a, Msg, Theme, Renderer>,{if container.hidden || container.entries.is_empty() {overlaid} else {let overlay = view_container(container);Element::new(Widget::<'a, Msg, _, _> { overlay, overlaid })}}impl<Msg, Theme, Renderer> iced::advanced::Widget<Msg, Theme, Renderer>for Widget<'_, Msg, Theme, Renderer>whereRenderer: iced::advanced::Renderer,{fn size(&self) -> iced::Size<iced::Length> {self.overlaid.as_widget().size()}fn layout(&self,tree: &mut iced_core::widget::Tree,renderer: &Renderer,limits: &iced_core::layout::Limits,) -> iced_core::layout::Node {self.overlaid.as_widget().layout(&mut tree.children[0],renderer,limits,)}fn update(&mut self,state: &mut Tree,event: &Event,layout: Layout<'_>,cursor: mouse::Cursor,renderer: &Renderer,clipboard: &mut dyn Clipboard,shell: &mut Shell<'_, Msg>,viewport: &Rectangle,) {self.overlaid.as_widget_mut().update(&mut state.children[0],event,layout,cursor,renderer,clipboard,shell,viewport,);}fn draw(&self,tree: &iced_core::widget::Tree,renderer: &mut Renderer,theme: &Theme,style: &iced_core::renderer::Style,layout: iced_core::Layout<'_>,cursor: iced_core::mouse::Cursor,viewport: &iced::Rectangle,) {self.overlaid.as_widget().draw(&tree.children[0],renderer,theme,style,layout,cursor,viewport,)}fn children(&self) -> Vec<Tree> {vec![Tree::new(&self.overlaid), Tree::new(&self.overlay)]}fn diff(&self, tree: &mut Tree) {tree.diff_children(&[&self.overlaid, &self.overlay]);}fn operate(&self,state: &mut Tree,layout: Layout<'_>,renderer: &Renderer,operation: &mut dyn Operation,) {operation.container(None, layout.bounds(), &mut |operation| {self.overlaid.as_widget().operate(&mut state.children[0],layout,renderer,operation,);});}fn mouse_interaction(&self,state: &Tree,layout: Layout<'_>,cursor: mouse::Cursor,viewport: &Rectangle,renderer: &Renderer,) -> mouse::Interaction {self.overlaid.as_widget().mouse_interaction(&state.children[0],layout,cursor,viewport,renderer,)}fn overlay<'a>(&'a mut self,tree: &'a mut Tree,layout: Layout<'a>,renderer: &Renderer,viewport: &Rectangle,translation: Vector,) -> Option<overlay::Element<'a, Msg, Theme, Renderer>> {let (content_tree, toast_tree) = tree.children.split_at_mut(1);let content_overlay = self.overlaid.as_widget_mut().overlay(&mut content_tree[0],layout,renderer,viewport,translation,);let toast_overlay = Overlay {overlay: &mut self.overlay,tree: &mut toast_tree[0],position: layout.bounds().position() + translation,bounds: layout.bounds(),};let toast_overlay = overlay::Element::new(Box::new(toast_overlay));let overlays = content_overlay.into_iter().chain(std::iter::once(toast_overlay)).collect::<Vec<_>>();Some(overlay::Group::with_children(overlays).overlay())}}struct Overlay<'a, 'b, Msg, Theme, Renderer> {overlay: &'b mut Element<'a, Msg, Theme, Renderer>,tree: &'b mut Tree,position: Point,bounds: Rectangle,}impl<'a, Msg, Theme, Renderer> overlay::Overlay<Msg, Theme, Renderer>for Overlay<'a, '_, Msg, Theme, Renderer>whereRenderer: iced::advanced::Renderer,{fn layout(&mut self, renderer: &Renderer, _bounds: Size) -> layout::Node {let bounds = self.bounds.size();let limits = layout::Limits::new(Size::ZERO, bounds);layout::Node::with_children(bounds,vec![self.overlay.as_widget().layout(self.tree, renderer, &limits).align(// TODO parameterizeAlignment::End,Alignment::End,bounds,)],).translate([self.position.x, self.position.y])}fn draw(&self,renderer: &mut Renderer,theme: &Theme,style: &renderer::Style,layout: Layout<'_>,cursor: mouse::Cursor,) {let viewport = layout.bounds();self.overlay.as_widget().draw(self.tree,renderer,theme,style,layout.children().next().unwrap(),cursor,&viewport,)}fn update(&mut self,event: &Event,layout: Layout<'_>,cursor: mouse::Cursor,renderer: &Renderer,clipboard: &mut dyn Clipboard,shell: &mut Shell<'_, Msg>,) {let viewport = layout.bounds();self.overlay.as_widget_mut().update(self.tree,event,layout.children().next().unwrap(),cursor,renderer,clipboard,shell,&viewport,);}fn operate(&mut self,layout: Layout<'_>,renderer: &Renderer,operation: &mut dyn Operation,) {operation.container(None, layout.bounds(), &mut |operation| {self.overlay.as_widget().operate(self.tree,layout.children().next().unwrap(),renderer,operation,);});}fn mouse_interaction(&self,layout: Layout<'_>,cursor: mouse::Cursor,renderer: &Renderer,) -> mouse::Interaction {let viewport = layout.bounds();self.overlay.as_widget().mouse_interaction(self.tree,layout.children().next().unwrap(),cursor,&viewport,renderer,)}} - edit in iced_expl_widget/src/lib.rs at line 2
pub mod report; - edit in iced_expl_widget/Cargo.toml at line 20
workspace = true[dependencies.jiff] - edit in Cargo.lock at line 2105
"jiff",