5O4FWCFP4ZPAS7WKSYPHN76ML3O2S4JUOYWOV2ETD4TF2H6KZ6AQC KM5PSZ4A2FJOPHJA6RC7LHZAUXLQDZDQC2DVSE5YUORLFIPZO74QC 23SFYK4Q5NKBPJG53PQNPWQH6UOUU2YKJEL7RLXYBRLJOJYV7AWQC PKJCFSBMXXA2H3US47IJEB7QMIYLEKTLGWQUYEZSKCDODDQTD6HQC XIASAP3GXH7YUHGWSFFLJQMLOM5C6GMI3AZHGTT2CFGPNV2QU5BAC SASAN2XCWDQ2VEHZ7TAQEN2R3Y7AG7JUGEFVRL4DZAGHXDFEZFRQC KWTBNTO3QUUE2YADF6SYW6G6ZOKYEWRJQKIWDGZXR33S3YNDVIZQC UR4J677RWA3OFG6HQTD46BUUE5YFPSBEFCJAEM5OMT4V5A7SBNNQC FJSVMFB4FRZV6VXQTQ3FWY3GRHSM5RLYNCZ67JIDN254CVY7QFOQC 3XRG4BB6V5V4DICZCMOZMLQNTANWKPO7BBRATTXOZLRNSEUQIA5AC WAOGSCOJ5A372BZKHEYD2BCDBCENNVLFYW3INKUOOAZMDADDIFIQC EJPSD5XO43DWUBBZGNQMY4TMCAXL5EWCGX3OEHUERQ5GRASGWQLQC YK3MOJJLRYEKZ4FUCNJ3YKMTKOINWIYOJKR3ER7IRSGTC7O6FJZQC 7WCB5YQJJZIPUAFHTCQBWNI6ZM5XMIQJAKTLYTR7NOR5NKESRMDQC AZ5D2LQUSYVWVEP7ISFDSZTMZ65UEHZATILMDQ4TYLCKJH4Q3TIAC CULHFNIVQ3ATML2W3Z45RARZ2LHGXONYTGGN2ETWAAMV7R3Y67AQC LFEMJYYDO45ASMQSOJ3TNID7B5UZXDHB3NWFZJXWOAWNBS6GMDEAC MYGIBRRHHXPKVRAMQQRJTZH74L2XOK3SF7J57JPCRKSVRLZ2D6NQC 6YZAVBWU6E5FYOI5JGEIPXGZLIKAW6LS2AOFIQWEE5DMOPPCD5PQC Container::NavNonSelectedSection => container::Style {border: Border {color: Color::TRANSPARENT,width: NAV_SECTION_BORDER_WIDTH,..default()},..default()},Container::NavSelectedSection => container::Style {border: Border {color: MAGENTA_LIGHT,width: NAV_SECTION_BORDER_WIDTH,..default()},..default()},
File::Decoded(decoded_file) => {view_decoded(state, nav, decoded_file, diff_selected)}
File::Decoded(decoded_file) => view_decoded(state,nav,decoded_file,diff_selected,select_sections,),
let sections =el(nav_scrollable(nav, sections_view).class(if diff_selected {theme::Scrollable::Selected} else {theme::Scrollable::Normal}));
let nav = if diff_selected && select_sections {nav_selectable(nav,sections_view,|| theme::Container::NavNonSelectedSection,|| theme::Container::NavSelectedSection,)} else {nav_scrollable(nav, sections_view)}.class(if diff_selected {theme::Scrollable::Selected} else {theme::Scrollable::Normal});let sections = el(nav);
if matches!(state.overall, PickSet::Include | PickSet::Exclude) {// Clear out the last partial states
// Set a pick for files that don't have it{let unset_file_pick = match state.overall {PickSet::Include | PickSet::Exclude => state.overall,PickSet::Partial => PickSet::Exclude,};
if matches!(state.overall, PickSet::Include | PickSet::Exclude) {// Clear out the last partial states
dbg!(&state);// Set a pick for files that don't have it{let unset_file_pick = match state.overall {PickSet::Include | PickSet::Exclude => state.overall,PickSet::Partial => PickSet::Exclude,};
state.files.insert(changed_file.clone(), state.overall);
if !state.files.contains_key(changed_file) {state.files.insert(changed_file.clone(), unset_file_pick);}}}// Set a pick for this file's changes that don't have it{let unset_pick = match state.files.get(&file).copied().unwrap_or(state.overall){PickSet::Include => Pick::Include,PickSet::Exclude | PickSet::Partial => Pick::Exclude,};let PartialFile { changes } =state.changes.entry(file.clone()).or_default();for change in changed_files.get(&file).unwrap() {if !changes.contains_key(change) {changes.insert(change.clone(), unset_pick);}
.unwrap_or(match pick {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude,
.unwrap_or_else(|| {if changed_files.get(&file).unwrap().len() > 1 {PickSet::Partial} else {match pick {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude,}}
use super::*;#[test]fn toggle_overall() {let mut state = State::default();assert_eq!(state.overall, PickSet::Include);let changed_files = repo::ChangedFiles::default();update(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Exclude);update(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Include);}// TODO add test in which files (and/or changes) are added and rm'd#[test]fn toggle_file() {let mut state = State::default();assert_eq!(state.overall, PickSet::Include);let file_a = "A";let file_b = "B";let file_c = "C";let changed_files = repo::ChangedFiles::from_iter([(file_a.to_owned(), repo::ChangedFile::default()),(file_b.to_owned(), repo::ChangedFile::default()),(file_c.to_owned(), repo::ChangedFile::default()),]);let toggle_file = |state: &mut State, file: &str| {update(state,Msg::ToggleFile {file: file.to_owned(),},&changed_files,)};let file = |state: &State, file: &str| *state.files.get(file).unwrap();// Exclude Atoggle_file(&mut state, file_a);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);// Exclude allupdate(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Exclude);// Include allupdate(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Include);// Include Atoggle_file(&mut state, file_a);assert_eq!(file(&state, file_a), PickSet::Include);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Include);// Exclude Atoggle_file(&mut state, file_a);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);// Exclude Btoggle_file(&mut state, file_b);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Exclude);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);// Exclude Ctoggle_file(&mut state, file_c);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Exclude);assert_eq!(file(&state, file_c), PickSet::Exclude);assert_eq!(state.overall, PickSet::Exclude);// Include Atoggle_file(&mut state, file_a);assert_eq!(file(&state, file_a), PickSet::Include);assert_eq!(file(&state, file_b), PickSet::Exclude);assert_eq!(file(&state, file_c), PickSet::Exclude);assert_eq!(state.overall, PickSet::Partial);// Include Btoggle_file(&mut state, file_b);assert_eq!(file(&state, file_a), PickSet::Include);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Exclude);assert_eq!(state.overall, PickSet::Partial);// Exclude allupdate(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Exclude);// Include allupdate(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Include);// Include Ctoggle_file(&mut state, file_c);assert_eq!(file(&state, file_a), PickSet::Include);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Include);// Exclude Atoggle_file(&mut state, file_a);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);// Exclude allupdate(&mut state, Msg::ToggleOverall, &changed_files);assert_eq!(state.overall, PickSet::Exclude);// Exclude Btoggle_file(&mut state, file_b);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(file(&state, file_b), PickSet::Exclude);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);}#[test]fn toggle_change() {let mut state = State::default();assert_eq!(state.overall, PickSet::Include);let file_a = "A";let file_b = "B";let file_c = "C";let change_a_1 = repo::ChangedFileDiff::Move;let change_b_1 = repo::ChangedFileDiff::Del;let change_b_2 = repo::ChangedFileDiff::Undel;let change_c_1 = repo::ChangedFileDiff::Add;let change_c_2 = repo::ChangedFileDiff::DelRoot;let change_c_3 = repo::ChangedFileDiff::AddRoot;let changed_files = repo::ChangedFiles::from_iter([(file_a.to_owned(),repo::ChangedFile::from_iter([change_a_1.clone()]),),(file_b.to_owned(),repo::ChangedFile::from_iter([change_b_1.clone(),change_b_2.clone(),]),),(file_c.to_owned(),repo::ChangedFile::from_iter([change_c_1.clone(),change_c_2.clone(),change_c_3.clone(),]),),]);let toggle_change =|state: &mut State, file: &str, change: &repo::ChangedFileDiff| {update(state,Msg::ToggleChange {file: file.to_owned(),change: change.clone(),},&changed_files,)};let file = |state: &State, file: &str| *state.files.get(file).unwrap();let change = |state: &State, file: &str, change: &repo::ChangedFileDiff| {*state.changes.get(file).unwrap().changes.get(change).unwrap()};// Exclude A.1toggle_change(&mut state, file_a, &change_a_1);assert_eq!(change(&state, file_a, &change_a_1), Pick::Exclude);assert_eq!(file(&state, file_a), PickSet::Exclude);assert_eq!(state.overall, PickSet::Partial);// Include A.1toggle_change(&mut state, file_a, &change_a_1);assert_eq!(change(&state, file_a, &change_a_1), Pick::Include);assert_eq!(file(&state, file_a), PickSet::Include);assert_eq!(state.overall, PickSet::Include);// Exclude B.1toggle_change(&mut state, file_b, &change_b_1);assert_eq!(change(&state, file_b, &change_b_1), Pick::Exclude);assert_eq!(file(&state, file_b), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Exclude B.2toggle_change(&mut state, file_b, &change_b_2);assert_eq!(change(&state, file_b, &change_b_2), Pick::Exclude);assert_eq!(file(&state, file_b), PickSet::Exclude);assert_eq!(state.overall, PickSet::Partial);// Include B.1toggle_change(&mut state, file_b, &change_b_1);assert_eq!(change(&state, file_b, &change_b_1), Pick::Include);assert_eq!(file(&state, file_b), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Include B.2toggle_change(&mut state, file_b, &change_b_2);assert_eq!(change(&state, file_b, &change_b_2), Pick::Include);assert_eq!(file(&state, file_b), PickSet::Include);assert_eq!(state.overall, PickSet::Include);// Exclude C.1toggle_change(&mut state, file_c, &change_c_1);assert_eq!(change(&state, file_c, &change_c_1), Pick::Exclude);assert_eq!(file(&state, file_c), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Exclude C.2toggle_change(&mut state, file_c, &change_c_2);assert_eq!(change(&state, file_c, &change_c_2), Pick::Exclude);assert_eq!(file(&state, file_c), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Exclude C.3toggle_change(&mut state, file_c, &change_c_3);assert_eq!(change(&state, file_c, &change_c_3), Pick::Exclude);assert_eq!(file(&state, file_c), PickSet::Exclude);assert_eq!(state.overall, PickSet::Partial);// Include C.1toggle_change(&mut state, file_c, &change_c_1);assert_eq!(change(&state, file_c, &change_c_1), Pick::Include);assert_eq!(file(&state, file_c), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Include C.2toggle_change(&mut state, file_c, &change_c_2);assert_eq!(change(&state, file_c, &change_c_2), Pick::Include);assert_eq!(file(&state, file_c), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Include C.3toggle_change(&mut state, file_c, &change_c_3);assert_eq!(change(&state, file_c, &change_c_3), Pick::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Include);// Exclude C.1toggle_change(&mut state, file_c, &change_c_1);assert_eq!(change(&state, file_c, &change_c_1), Pick::Exclude);assert_eq!(file(&state, file_c), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Exclude B.1toggle_change(&mut state, file_b, &change_b_1);assert_eq!(change(&state, file_b, &change_b_1), Pick::Exclude);assert_eq!(file(&state, file_b), PickSet::Partial);assert_eq!(state.overall, PickSet::Partial);// Include C.1toggle_change(&mut state, file_c, &change_c_1);assert_eq!(change(&state, file_c, &change_c_1), Pick::Include);assert_eq!(file(&state, file_c), PickSet::Include);assert_eq!(state.overall, PickSet::Partial);}
/// Same purpose as `to_record::ToggleFile`, but without file name for/// action filteringToRecordToggleSelectedFile,
/// Same purpose as `to_record::ToggleFile` or `to_record::ToggleChange`,/// but without file name for action filteringToRecordToggleSelectedFileOrChange,
ToRecordToggleSelectedFile,ToRecord(to_record::Msg::ToggleFile { file: _ }),
ToRecord(to_record::Msg::ToggleFile { file: _ })| ToRecord(to_record::Msg::ToggleChange { file: _, change: _ }),ToRecordToggleSelectedFileOrChange,) => true,(ToRecordToggleSelectedFileOrChange,ToRecord(to_record::Msg::ToggleFile { file: _ })| ToRecord(to_record::Msg::ToggleChange { file: _, change: _ }),
msg: Some(FilteredMsg::ToRecordToggleSelectedFile),
msg: Some(FilteredMsg::ToRecordToggleSelectedFileOrChange),}};let to_record_toggle_file_change = |next: to_record::Pick| {let label = match next {to_record::Pick::Include => "include selected change",to_record::Pick::Exclude => "exclude selected change",};Binding {key: "t",label,msg: Some(FilteredMsg::ToRecordToggleSelectedFileOrChange),
push_if_some(next_to_record_file_pick, to_record_toggle_file, ma);
// TODO maybe in here S+t should control file pick and lowercase// used for selected changelet _ = next_to_record_file_pick;// push_if_some(next_to_record_file_pick, to_record_toggle_file,// ma);push_if_some(next_to_record_file_change_pick,to_record_toggle_file_change,ma,);
let diff_ix = navigation.files_diffs.diffs_nav.get_selected_section_ix();let selected_diff = diff_ix.and_then(|diff_ix| {changed_files.get(path).and_then(|diffs| diffs.iter().nth(diff_ix))});let next_to_record_file_change_pick = selected_diff.map(|selected_diff| {to_record::next_file_change_pick(path,selected_diff,to_record,)});
let msg = to_record::Msg::ToggleFile { file: path.clone() };to_record::update(to_record, msg, &repo.changed_files);
if *diff_selected {let diff_ix = navigation.files_diffs.diffs_nav.get_selected_section_ix().unwrap();let selected_diff = repo.changed_files.get(path).and_then(|diffs| diffs.iter().nth(diff_ix)).cloned();if let Some(change) = selected_diff {let msg = to_record::Msg::ToggleChange {file: path.clone(),change,};to_record::update(to_record, msg, &repo.changed_files);}} else {let msg = to_record::Msg::ToggleFile { file: path.clone() };to_record::update(to_record, msg, &repo.changed_files);}
// The top edge of the section is visible, but the bottom// edge of is below the bottom edge of nav, scroll down
// The top edge of the section is above the bottom frame,// but the bottom edge of is below the// bottom frame, scroll down to see the rest of it
Some((state.offset_y + offset_delta, Delay::Start))} else if *offset > bottom_frame {// Scroll to the next section
Some((ix, state.offset_y + offset_delta, Delay::Start))} else if *offset > bottom_frame|| state.selected_section.map(|section| section < ix).unwrap_or_default(){// Section is below the bottom frame, or the previous// section is selected, scroll down to see this one
// The bottom edge of the section is visible, but the top// edge of is above the top edge of nav, scroll up
// The bottom edge of the section is below the top frame,// but the top edge of is above the top// farme, scroll up to see the rest of it
} else if offset + height < top_frame {
} else if offset + height < top_frame|| state.selected_section.map(|section| section > ix).unwrap_or_default(){// Section is above the top frame, or the following section// is selected, scroll up to see this one
}}impl<'a, Message, Theme, Renderer> NavScrollable<'a, Message, Theme, Renderer>whereMessage: 'a,Theme: 'a + Catalog + container::Catalog,Renderer: 'a + iced::advanced::Renderer,{/// Creates a new [`NavScrollable`] with section selection.pub fn with_selection(state: &'a State,children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,non_selected_section: impl Fn() -> <Theme as container::Catalog>::Class<'a>,selected_section: impl Fn() -> <Theme as container::Catalog>::Class<'a>,) -> Self {let selected_section_ix = state.select_fst_section();// Add border for selected sectionlet children: Vec<_> = children.into_iter().enumerate().map(|(ix, child)| {Element::from(if ix == selected_section_ix {container(child).class(selected_section())} else {container(child).class(non_selected_section())})}).collect();let content = iced::widget::column(children).into();NavScrollable {state,width: Length::Shrink,height: Length::Shrink,direction: Direction::default(),content,on_scroll: None,class: <Theme as Catalog>::default(),last_status: None,}.validate()
let inner = RefCell::into_inner(inner);let inner = StateInner {skip_sections,..inner};Self {inner: RefCell::new(inner),
let mut inner = inner.borrow_mut();if let Some(ix) = inner.selected_section {ix} else {debug_assert!(inner.skip_sections.len() < inner.section_heights.len(),"There has to be at least one non-skip section");let mut selected_section: usize = 0;while inner.skip_sections.contains(&selected_section) {selected_section += 1;}inner.selected_section = Some(selected_section);selected_section
}/// Get the index of selected section within the selectable sections only/// (the index range excludes `skip_sections`)pub fn get_selected_section_ix(&self) -> Option<usize> {let Self { inner } = self;let inner = inner.borrow();inner.selected_section.map(|ix| {ix - inner.skip_sections.iter().filter(|section| **section < ix).count()})
}pub fn nav_selectable<'a, Message, Theme, Renderer>(state: &'a nav_scrollable::State,children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,non_selected_section: impl Fn() -> <Theme as container::Catalog>::Class<'a>,selected_section: impl Fn() -> <Theme as container::Catalog>::Class<'a>,) -> NavScrollable<'a, Message, Theme, Renderer>whereMessage: 'a,Theme: 'a + nav_scrollable::Catalog + container::Catalog,Renderer: 'a + iced_core::Renderer,{NavScrollable::with_selection(state,children,non_selected_section,selected_section,)