|
3 | 3 | use std::collections::HashMap; |
4 | 4 |
|
5 | 5 | use iced::widget::Slider; |
| 6 | +use iced::widget::Space; |
6 | 7 | use iced::widget::TextInput; |
7 | 8 | use iced::widget::checkbox; |
8 | 9 | use iced::widget::text_input; |
9 | 10 |
|
10 | 11 | use crate::app::Editable; |
11 | 12 | use crate::app::SetConfigBufferFields; |
12 | 13 | use crate::app::SetConfigThemeFields; |
| 14 | +use crate::commands::Function; |
| 15 | +use crate::config::Shelly; |
13 | 16 | use crate::styles::delete_button_style; |
14 | 17 | use crate::styles::settings_add_button_style; |
15 | 18 | use crate::styles::settings_checkbox_style; |
@@ -204,16 +207,19 @@ pub fn settings_page(config: Config) -> Element<'static, Message> { |
204 | 207 | let theme_clone = theme.clone(); |
205 | 208 | let font_family = settings_item_column([ |
206 | 209 | settings_hint_text(theme.clone(), "Set Font family"), |
207 | | - text_input("Font family", &config.theme.font.unwrap_or("".to_string())) |
208 | | - .on_input(move |input: String| { |
209 | | - Message::SetConfig(SetConfigFields::SetThemeFields(SetConfigThemeFields::Font( |
210 | | - input, |
211 | | - ))) |
212 | | - }) |
213 | | - .on_submit(Message::WriteConfig(false)) |
214 | | - .width(Length::Fill) |
215 | | - .style(move |_, _| settings_text_input_item_style(&theme_clone)) |
216 | | - .into(), |
| 210 | + text_input( |
| 211 | + "Font family", |
| 212 | + &config.theme.font.clone().unwrap_or("".to_string()), |
| 213 | + ) |
| 214 | + .on_input(move |input: String| { |
| 215 | + Message::SetConfig(SetConfigFields::SetThemeFields(SetConfigThemeFields::Font( |
| 216 | + input, |
| 217 | + ))) |
| 218 | + }) |
| 219 | + .on_submit(Message::WriteConfig(false)) |
| 220 | + .width(Length::Fill) |
| 221 | + .style(move |_, _| settings_text_input_item_style(&theme_clone)) |
| 222 | + .into(), |
217 | 223 | notice_item(theme.clone(), "What font rustcast should use"), |
218 | 224 | ]); |
219 | 225 |
|
@@ -372,12 +378,18 @@ pub fn settings_page(config: Config) -> Element<'static, Message> { |
372 | 378 | text_clr.into(), |
373 | 379 | bg_clr.into(), |
374 | 380 | settings_hint_text(theme.clone(), "Aliases"), |
375 | | - aliases_item(config.aliases, &theme), |
| 381 | + aliases_item(config.aliases.clone(), &theme), |
376 | 382 | settings_hint_text(theme.clone(), "Modes"), |
377 | | - modes_item(config.modes, &theme), |
| 383 | + modes_item(config.modes.clone(), &theme), |
| 384 | + settings_hint_text(theme.clone(), "Search Directories"), |
| 385 | + search_dirs_item(&theme, config.search_dirs.clone()), |
| 386 | + Space::new().height(30).into(), |
| 387 | + settings_hint_text(theme.clone(), "Shell commands"), |
| 388 | + shell_commands_item(config.shells.clone(), theme.clone()), |
378 | 389 | Row::from_iter([ |
379 | 390 | savebutton(theme.clone()), |
380 | 391 | default_button(theme.clone()), |
| 392 | + copy_config_button(config), |
381 | 393 | wiki_button(theme.clone()), |
382 | 394 | ]) |
383 | 395 | .spacing(5) |
@@ -423,18 +435,34 @@ fn default_button(theme: Theme) -> Element<'static, Message> { |
423 | 435 |
|
424 | 436 | fn wiki_button(theme: Theme) -> Element<'static, Message> { |
425 | 437 | Button::new( |
426 | | - Text::new("Open the wiki") |
| 438 | + Text::new("Open file") |
427 | 439 | .align_x(Alignment::Center) |
428 | 440 | .width(Length::Fill) |
429 | 441 | .font(theme.font()), |
430 | 442 | ) |
431 | 443 | .style(move |_, _| settings_save_button_style(&theme)) |
432 | 444 | .width(Length::Fill) |
433 | | - .on_press(Message::RunFunction( |
434 | | - crate::commands::Function::OpenWebsite( |
435 | | - "https://github.com/RustCastLabs/rustcast/wiki".to_string(), |
| 445 | + .on_press(Message::RunFunction(crate::commands::Function::OpenApp( |
| 446 | + std::env::var("HOME").unwrap_or("".to_string()) + "/.config/rustcast/config.toml", |
| 447 | + ))) |
| 448 | + .into() |
| 449 | +} |
| 450 | + |
| 451 | +fn copy_config_button(config: Box<Config>) -> Element<'static, Message> { |
| 452 | + let theme = config.theme.clone(); |
| 453 | + Button::new( |
| 454 | + Text::new("Copy config") |
| 455 | + .align_x(Alignment::Center) |
| 456 | + .width(Length::Fill) |
| 457 | + .font(theme.font()), |
| 458 | + ) |
| 459 | + .style(move |_, _| settings_save_button_style(&theme)) |
| 460 | + .width(Length::Fill) |
| 461 | + .on_press(Message::RunFunction(Function::CopyToClipboard( |
| 462 | + crate::clipboard::ClipBoardContentType::Text( |
| 463 | + toml::to_string(&config).unwrap_or("".to_string()), |
436 | 464 | ), |
437 | | - )) |
| 465 | + ))) |
438 | 466 | .into() |
439 | 467 | } |
440 | 468 |
|
@@ -540,6 +568,47 @@ fn aliases_item(aliases: HashMap<String, String>, theme: &Theme) -> Element<'sta |
540 | 568 | .into() |
541 | 569 | } |
542 | 570 |
|
| 571 | +fn search_dirs_item(theme: &Theme, search_dirs: Vec<String>) -> Element<'static, Message> { |
| 572 | + let theme_clone = theme.clone(); |
| 573 | + let search_dirs = search_dirs.clone(); |
| 574 | + Column::from_iter([ |
| 575 | + container( |
| 576 | + Column::from_iter(search_dirs.iter().map(|dir| { |
| 577 | + let theme_clone_2 = theme.clone(); |
| 578 | + let directory = dir.clone(); |
| 579 | + container( |
| 580 | + Row::from_iter([ |
| 581 | + dir_picker_button(directory, dir, theme_clone.clone()).into(), |
| 582 | + Button::new("Delete") |
| 583 | + .on_press(Message::SetConfig(SetConfigFields::SearchDirs( |
| 584 | + Editable::Delete(dir.clone()), |
| 585 | + ))) |
| 586 | + .style(move |_, _| delete_button_style(&theme_clone_2)) |
| 587 | + .into(), |
| 588 | + ]) |
| 589 | + .spacing(10) |
| 590 | + .align_y(Alignment::Center), |
| 591 | + ) |
| 592 | + .width(Length::Fill) |
| 593 | + .align_x(Alignment::Center) |
| 594 | + .into() |
| 595 | + })) |
| 596 | + .spacing(10), |
| 597 | + ) |
| 598 | + .height(Length::Fill) |
| 599 | + .width(Length::Fill) |
| 600 | + .align_x(Alignment::Center) |
| 601 | + .align_y(Alignment::Center) |
| 602 | + .into(), |
| 603 | + dir_adder_button("+", theme.to_owned()).into(), |
| 604 | + ]) |
| 605 | + .spacing(10) |
| 606 | + .height(Length::Fill) |
| 607 | + .width(Length::Fill) |
| 608 | + .align_x(Alignment::Center) |
| 609 | + .into() |
| 610 | +} |
| 611 | + |
543 | 612 | fn text_input_cell(text: String, theme: &Theme, placeholder: &str) -> TextInput<'static, Message> { |
544 | 613 | text_input(placeholder, &text) |
545 | 614 | .font(theme.font()) |
@@ -610,3 +679,212 @@ fn modes_item(modes: HashMap<String, String>, theme: &Theme) -> Element<'static, |
610 | 679 | .align_x(Alignment::Center) |
611 | 680 | .into() |
612 | 681 | } |
| 682 | + |
| 683 | +fn dir_picker_button(directory: String, dir: &str, theme: Theme) -> Button<'static, Message> { |
| 684 | + let home = std::env::var("HOME").unwrap_or("/".to_string()); |
| 685 | + Button::new(Text::new(dir.to_owned().replace(&home, "~"))) |
| 686 | + .on_press_with(move || { |
| 687 | + rfd::FileDialog::new() |
| 688 | + .set_directory(home.clone()) |
| 689 | + .set_can_create_directories(false) |
| 690 | + .pick_folder() |
| 691 | + .map(|path| { |
| 692 | + let new = path.to_str().unwrap_or("").to_string(); |
| 693 | + Message::SetConfig(SetConfigFields::SearchDirs(Editable::Update { |
| 694 | + old: directory.clone(), |
| 695 | + new, |
| 696 | + })) |
| 697 | + }) |
| 698 | + .unwrap_or(Message::SetConfig(SetConfigFields::SearchDirs( |
| 699 | + Editable::Update { |
| 700 | + old: directory.clone(), |
| 701 | + new: directory.clone(), |
| 702 | + }, |
| 703 | + ))) |
| 704 | + }) |
| 705 | + .style(move |_, _| settings_add_button_style(&theme.clone())) |
| 706 | +} |
| 707 | + |
| 708 | +fn dir_adder_button(dir: &str, theme: Theme) -> Button<'static, Message> { |
| 709 | + Button::new(Text::new(dir.to_owned())) |
| 710 | + .on_press_with(move || { |
| 711 | + rfd::FileDialog::new() |
| 712 | + .set_directory(std::env::var("HOME").unwrap_or("/".to_string())) |
| 713 | + .set_can_create_directories(false) |
| 714 | + .pick_folder() |
| 715 | + .map(|path| { |
| 716 | + let new = path.to_str().unwrap_or("").to_string(); |
| 717 | + Message::SetConfig(SetConfigFields::SearchDirs(Editable::Create(new))) |
| 718 | + }) |
| 719 | + .unwrap_or(Message::SetConfig(SetConfigFields::SearchDirs( |
| 720 | + Editable::Create(String::new()), |
| 721 | + ))) |
| 722 | + }) |
| 723 | + .style(move |_, _| settings_add_button_style(&theme.clone())) |
| 724 | +} |
| 725 | + |
| 726 | +fn shell_commands_item(shells: Vec<Shelly>, theme: Theme) -> Element<'static, Message> { |
| 727 | + let mut col = |
| 728 | + Column::from_iter(shells.iter().map(|x| x.editable_render(theme.clone()))).spacing(30); |
| 729 | + |
| 730 | + let theme_clone = theme.clone(); |
| 731 | + |
| 732 | + col = col |
| 733 | + .push( |
| 734 | + Button::new( |
| 735 | + Text::new("+") |
| 736 | + .align_x(Alignment::Center) |
| 737 | + .align_y(Alignment::Center), |
| 738 | + ) |
| 739 | + .style(move |_, _| settings_add_button_style(&theme_clone.clone())) |
| 740 | + .on_press(Message::SetConfig(SetConfigFields::ShellCommands( |
| 741 | + Editable::Create(Shelly::default()), |
| 742 | + ))), |
| 743 | + ) |
| 744 | + .width(Length::Fill) |
| 745 | + .align_x(Alignment::Center); |
| 746 | + |
| 747 | + col.into() |
| 748 | +} |
| 749 | + |
| 750 | +impl Shelly { |
| 751 | + pub fn editable_render(&self, theme: Theme) -> Element<'static, Message> { |
| 752 | + let shell = self.to_owned(); |
| 753 | + Column::from_iter([ |
| 754 | + tuple_row( |
| 755 | + shellcommand_hint_text(theme.clone(), "Display name"), |
| 756 | + text_input_cell(self.alias.clone(), &theme, "Display Name") |
| 757 | + .on_input({ |
| 758 | + let shell = shell.clone(); |
| 759 | + move |input| { |
| 760 | + let old = shell.clone(); |
| 761 | + let mut new = old.clone(); |
| 762 | + new.alias = input; |
| 763 | + Message::SetConfig(SetConfigFields::ShellCommands(Editable::Update { |
| 764 | + old, |
| 765 | + new, |
| 766 | + })) |
| 767 | + } |
| 768 | + }) |
| 769 | + .into(), |
| 770 | + ) |
| 771 | + .into(), |
| 772 | + tuple_row( |
| 773 | + shellcommand_hint_text(theme.clone(), "Search name"), |
| 774 | + text_input_cell(self.alias_lc.clone(), &theme, "Search Name") |
| 775 | + .on_input({ |
| 776 | + let shell = shell.clone(); |
| 777 | + move |input| { |
| 778 | + let old = shell.clone(); |
| 779 | + let mut new = old.clone(); |
| 780 | + new.alias_lc = input; |
| 781 | + Message::SetConfig(SetConfigFields::ShellCommands(Editable::Update { |
| 782 | + old, |
| 783 | + new, |
| 784 | + })) |
| 785 | + } |
| 786 | + }) |
| 787 | + .into(), |
| 788 | + ) |
| 789 | + .into(), |
| 790 | + tuple_row( |
| 791 | + shellcommand_hint_text(theme.clone(), "Command"), |
| 792 | + text_input_cell(self.command.clone(), &theme, "Command") |
| 793 | + .on_input({ |
| 794 | + let shell = shell.clone(); |
| 795 | + move |input| { |
| 796 | + let old = shell.clone(); |
| 797 | + let mut new = old.clone(); |
| 798 | + new.command = input; |
| 799 | + Message::SetConfig(SetConfigFields::ShellCommands(Editable::Update { |
| 800 | + old, |
| 801 | + new, |
| 802 | + })) |
| 803 | + } |
| 804 | + }) |
| 805 | + .into(), |
| 806 | + ) |
| 807 | + .into(), |
| 808 | + tuple_row( |
| 809 | + shellcommand_hint_text(theme.clone(), "Icon File"), |
| 810 | + text_input_cell( |
| 811 | + self.icon_path.clone().unwrap_or("".to_string()), |
| 812 | + &theme, |
| 813 | + "Icon path", |
| 814 | + ) |
| 815 | + .on_input({ |
| 816 | + let shell = shell.clone(); |
| 817 | + move |input| { |
| 818 | + let old = shell.clone(); |
| 819 | + let mut new = old.clone(); |
| 820 | + new.icon_path = if input.is_empty() { None } else { Some(input) }; |
| 821 | + Message::SetConfig(SetConfigFields::ShellCommands(Editable::Update { |
| 822 | + old, |
| 823 | + new, |
| 824 | + })) |
| 825 | + } |
| 826 | + }) |
| 827 | + .into(), |
| 828 | + ) |
| 829 | + .into(), |
| 830 | + tuple_row( |
| 831 | + shellcommand_hint_text(theme.clone(), "Hotkey"), |
| 832 | + text_input_cell( |
| 833 | + self.hotkey.clone().unwrap_or("".to_string()), |
| 834 | + &theme, |
| 835 | + "Hotkey", |
| 836 | + ) |
| 837 | + .on_input({ |
| 838 | + let shell = shell.clone(); |
| 839 | + move |input| { |
| 840 | + let old = shell.clone(); |
| 841 | + let mut new = old.clone(); |
| 842 | + new.hotkey = Some(input); |
| 843 | + Message::SetConfig(SetConfigFields::ShellCommands(Editable::Update { |
| 844 | + old, |
| 845 | + new, |
| 846 | + })) |
| 847 | + } |
| 848 | + }) |
| 849 | + .into(), |
| 850 | + ) |
| 851 | + .into(), |
| 852 | + tuple_row( |
| 853 | + Button::new("Delete") |
| 854 | + .on_press(Message::SetConfig(SetConfigFields::ShellCommands( |
| 855 | + Editable::Delete(self.clone()), |
| 856 | + ))) |
| 857 | + .style({ |
| 858 | + let theme = theme.clone(); |
| 859 | + move |_, _| delete_button_style(&theme) |
| 860 | + }) |
| 861 | + .into(), |
| 862 | + notice_item(theme.clone(), "Icon path and hotkey are optional"), |
| 863 | + ) |
| 864 | + .into(), |
| 865 | + ]) |
| 866 | + .spacing(10) |
| 867 | + .height(Length::Fill) |
| 868 | + .width(Length::Fill) |
| 869 | + .into() |
| 870 | + } |
| 871 | +} |
| 872 | + |
| 873 | +fn tuple_row( |
| 874 | + left: Element<'static, Message>, |
| 875 | + right: Element<'static, Message>, |
| 876 | +) -> Row<'static, Message> { |
| 877 | + Row::from_iter([left, right]) |
| 878 | + .spacing(10) |
| 879 | + .width(Length::Fill) |
| 880 | +} |
| 881 | + |
| 882 | +fn shellcommand_hint_text(theme: Theme, text: impl ToString) -> Element<'static, Message> { |
| 883 | + let text = text.to_string(); |
| 884 | + |
| 885 | + Text::new(text) |
| 886 | + .font(theme.font()) |
| 887 | + .color(theme.text_color(0.7)) |
| 888 | + .width(WINDOW_WIDTH * 0.3) |
| 889 | + .into() |
| 890 | +} |
0 commit comments