diff --git a/CHANGELOG.md b/CHANGELOG.md index a85dd70..7e42239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ to better preserve the files' executable permissions. For Steam roots, this also supports shortcuts to non-Steam games, where the placeholder will map to the shortcut's dynamic app ID. * Paths may now use the `` placeholder. + * GUI: Custom games can now be expanded and collapsed. * Fixed: * Files on Windows network shares were not backed up correctly. For example, a file identified as `\\localhost\share\test.txt` diff --git a/src/gui/app.rs b/src/gui/app.rs index d78f8ca..36c1c0a 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -1083,6 +1083,7 @@ impl App { prefer_alias: false, files: standard.files.keys().cloned().collect(), registry: standard.registry.keys().cloned().collect(), + expanded: true, } } else { CustomGame { @@ -1092,6 +1093,7 @@ impl App { prefer_alias: false, files: vec![], registry: vec![], + expanded: true, } }; @@ -1114,6 +1116,7 @@ impl App { prefer_alias: true, files: vec![], registry: vec![], + expanded: true, }; self.text_histories.add_custom_game(&game); @@ -1841,6 +1844,11 @@ impl App { self.save_config(); Task::none() } + Message::ToggleCustomGameExpanded { index, expanded } => { + self.config.custom_games[index].expanded = expanded; + self.save_config(); + Task::none() + } Message::TogglePrimaryManifestEnabled { enabled } => { self.config.manifest.enable = enabled; self.save_config(); diff --git a/src/gui/button.rs b/src/gui/button.rs index c09bdd3..674ae39 100644 --- a/src/gui/button.rs +++ b/src/gui/button.rs @@ -487,3 +487,20 @@ pub fn show_game_notes<'a>(game: String, notes: Vec) -> Element< Some(style::Button::Bare), ) } + +pub fn expand<'a>(expanded: bool, on_press: Message) -> Element<'a> { + Button::new( + (if expanded { + Icon::KeyboardArrowDown + } else { + Icon::KeyboardArrowRight + }) + .text_small(), + ) + .on_press(on_press) + .class(style::Button::Primary) + .padding(5) + .height(25) + .width(25) + .into() +} diff --git a/src/gui/common.rs b/src/gui/common.rs index 13af486..b9bc3aa 100644 --- a/src/gui/common.rs +++ b/src/gui/common.rs @@ -169,6 +169,10 @@ pub enum Message { index: usize, enabled: bool, }, + ToggleCustomGameExpanded { + index: usize, + expanded: bool, + }, TogglePrimaryManifestEnabled { enabled: bool, }, diff --git a/src/gui/editor.rs b/src/gui/editor.rs index 72d8b4b..b13df1f 100644 --- a/src/gui/editor.rs +++ b/src/gui/editor.rs @@ -253,72 +253,84 @@ pub fn custom_games<'a>( .padding(padding::top(0).bottom(5).left(15).right(15)) .spacing(10), |parent, (i, x)| { - parent.push( - Container::new( - Column::new() - .padding(5) - .spacing(5) + parent.push({ + let mut content = Column::new().padding(5).spacing(5).push( + Row::new() + .spacing(20) + .align_y(iced::Alignment::Center) + .push(button::expand( + x.expanded, + Message::ToggleCustomGameExpanded { + index: i, + expanded: !x.expanded, + }, + )) .push( Row::new() + .width(110) .spacing(20) - .align_y(iced::Alignment::Center) - .push( - Row::new() - .width(110) - .spacing(20) - .align_y(Alignment::Center) - .push( - checkbox("", config.is_custom_game_enabled(i), move |enabled| { - Message::ToggleCustomGameEnabled { index: i, enabled } - }) - .spacing(0) - .class(style::Checkbox), - ) - .push(button::move_up(Message::EditedCustomGame, i)) - .push(button::move_down( - Message::EditedCustomGame, - i, - config.custom_games.len(), - )), - ) - .push(histories.input(UndoSubject::CustomGameName(i))) + .align_y(Alignment::Center) .push( - pick_list(CustomGameKind::ALL, Some(config.custom_games[i].kind()), move |v| { - Message::SelectedCustomGameKind(i, v) + checkbox("", config.is_custom_game_enabled(i), move |enabled| { + Message::ToggleCustomGameEnabled { index: i, enabled } }) - .class(style::PickList::Primary) - .width(100), + .spacing(0) + .class(style::Checkbox), ) - .push( - Tooltip::new( - button::refresh_custom_game( - Message::Backup(BackupPhase::Start { - games: Some(vec![config.custom_games[i].name.clone()]), - preview: true, - repair: false, - }), - operating, - config.is_custom_game_individually_scannable(i), - ), - text(TRANSLATOR.preview_button_in_custom_mode()).size(16), - tooltip::Position::Top, - ) - .gap(5) - .class(style::Container::Tooltip), - ) - .push(button::delete(Message::EditedCustomGame, i)), + .push(button::move_up(Message::EditedCustomGame, i)) + .push(button::move_down( + Message::EditedCustomGame, + i, + config.custom_games.len(), + )), + ) + .push(histories.input(UndoSubject::CustomGameName(i))) + .push( + pick_list(CustomGameKind::ALL, Some(config.custom_games[i].kind()), move |v| { + Message::SelectedCustomGameKind(i, v) + }) + .class(style::PickList::Primary) + .width(100), + ) + .push( + Tooltip::new( + button::refresh_custom_game( + Message::Backup(BackupPhase::Start { + games: Some(vec![config.custom_games[i].name.clone()]), + preview: true, + repair: false, + }), + operating, + config.is_custom_game_individually_scannable(i), + ), + text(TRANSLATOR.preview_button_in_custom_mode()).size(16), + tooltip::Position::Top, + ) + .gap(5) + .class(style::Container::Tooltip), ) + .push(button::delete(Message::EditedCustomGame, i)), + ); + + if x.expanded { + let left_side = 165; + + content = content .push_if(config.custom_games[i].kind() == CustomGameKind::Alias, move || { Row::new() .spacing(10) .align_y(Alignment::Center) - .push(Column::new().width(120).push(text(TRANSLATOR.original_name_field()))) + .push( + Column::new() + .width(left_side) + .push(text(TRANSLATOR.original_name_field())), + ) .push(histories.input(UndoSubject::CustomGameAlias(i))) }) .push_if(config.custom_games[i].kind() == CustomGameKind::Alias, || { Row::new() .spacing(10) - .push(horizontal_space().width(120)) + .push(horizontal_space().width(left_side)) .push(checkbox( TRANSLATOR.prefer_alias_display(), config.custom_games[i].prefer_alias, @@ -327,7 +339,12 @@ pub fn custom_games<'a>( }) .push_if(config.custom_games[i].kind() == CustomGameKind::Game, || { Row::new() - .push(Column::new().width(130).push(text(TRANSLATOR.custom_files_label()))) + .spacing(10) + .push( + Column::new() + .width(left_side) + .push(text(TRANSLATOR.custom_files_label())), + ) .push( x.files .iter() @@ -357,7 +374,12 @@ pub fn custom_games<'a>( }) .push_if(config.custom_games[i].kind() == CustomGameKind::Game, || { Row::new() - .push(Column::new().width(130).push(text(TRANSLATOR.custom_registry_label()))) + .spacing(10) + .push( + Column::new() + .width(left_side) + .push(text(TRANSLATOR.custom_registry_label())), + ) .push( x.registry .iter() @@ -388,11 +410,13 @@ pub fn custom_games<'a>( }) .push(button::add_nested(Message::EditedCustomGameRegistry, i)), ) - }), - ) - .id(iced::widget::container::Id::new(config.custom_games[i].name.clone())) - .class(style::Container::GameListEntry), - ) + }); + } + + Container::new(content) + .id(iced::widget::container::Id::new(config.custom_games[i].name.clone())) + .class(style::Container::GameListEntry) + }) }, ); diff --git a/src/gui/file_tree.rs b/src/gui/file_tree.rs index d1cc7d6..852fdd7 100644 --- a/src/gui/file_tree.rs +++ b/src/gui/file_tree.rs @@ -5,6 +5,7 @@ use iced::{padding, Alignment}; use crate::{ gui::{ badge::Badge, + button, common::{Message, TreeNodeKey}, icon::Icon, style, @@ -219,24 +220,13 @@ impl FileTreeNode { .align_y(Alignment::Center) .padding(padding::left(35 * level).right(10)) .spacing(10) - .push( - Button::new( - (if expanded { - Icon::KeyboardArrowDown - } else { - Icon::KeyboardArrowRight - }) - .text_small(), - ) - .on_press(Message::ToggleGameListEntryTreeExpanded { + .push(button::expand( + expanded, + Message::ToggleGameListEntryTreeExpanded { name: game_name.to_string(), keys: self.keys.clone(), - }) - .class(style::Button::Primary) - .padding(5) - .height(25) - .width(25), - ) + }, + )) .push_maybe(make_enabler()) .push(text(if label.is_empty() && self.node_type == FileTreeNodeType::File { "/".to_string() diff --git a/src/resource/config.rs b/src/resource/config.rs index ae5ca00..1fe54ee 100644 --- a/src/resource/config.rs +++ b/src/resource/config.rs @@ -1123,6 +1123,8 @@ pub struct CustomGame { pub files: Vec, /// Any registry keys you want to back up. pub registry: Vec, + #[serde(skip)] + pub expanded: bool, } impl CustomGame { @@ -1523,6 +1525,7 @@ impl Config { prefer_alias: false, files: vec![], registry: vec![], + expanded: false, }); } @@ -2086,6 +2089,7 @@ mod tests { prefer_alias: false, files: vec![], registry: vec![], + expanded: false, }, CustomGame { name: s("Custom Game 2"), @@ -2094,6 +2098,7 @@ mod tests { prefer_alias: false, files: vec![s("Custom File 1"), s("Custom File 2"), s("Custom File 2"),], registry: vec![s("Custom Registry 1"), s("Custom Registry 2"), s("Custom Registry 2"),], + expanded: false, }, ], }, @@ -2274,6 +2279,7 @@ customGames: prefer_alias: false, files: vec![], registry: vec![], + expanded: false, }, CustomGame { name: s("Custom Game 2"), @@ -2282,6 +2288,7 @@ customGames: prefer_alias: false, files: vec![s("Custom File 1"), s("Custom File 2"), s("Custom File 2"),], registry: vec![s("Custom Registry 1"), s("Custom Registry 2"), s("Custom Registry 2"),], + expanded: false, }, CustomGame { name: s("Alias"), @@ -2290,6 +2297,7 @@ customGames: prefer_alias: false, files: vec![], registry: vec![], + expanded: false, }, ], })