Программирование кнопки Open

Самое время запрограммировать логику работы кнопок Open, Save, Save As в нашем пользовательском интерфейсе. Начнём с реализации кнопки Open, создав новый метод open_file(), который будет вызван из метода connect_events().

Кнопка Open

Функция кнопки open_file() получит доступ к переменной current_file, которая будет обновлена после успешного открытия выбранного файла.


# #![allow(unused_variables)]
#fn main() {
pub fn connect_events(self) -> ConnectedApp {
    // Внешнее состояние, которое доступно для событий.
    let current_file = Arc::new(RwLock::new(None));

    // Подсоединить события, обрабатываемые пользовательским интерфейсом.
    self.editor_changed(current_file.clone(), &self.header.save.clone());
    self.open_file(current_file.clone());

    // Обернуть `ConnectedApp` вокруг `App` для того, чтобы дать возможность
    // разработчику выполнить программу.
    ConnectedApp(self)
}
#}

connect_clicked()

Метод open_file() будет получать:

  • ссылки на буфер редактора и писать информацию из открытого файла в буфер.
  • ссылки на панель web просмотра, так что мы сможем обновлять её после открытия файла.
  • ссылку на заголовочную панель, так что мы сможем обновлять название.
  • кнопку Open, так что мы сможем отобразить событие connect_clicked() на неё.

# #![allow(unused_variables)]
#fn main() {
fn open_file(&self, current_file: Arc<RwLock<Option<ActiveMetadata>>>) {
    let editor = self.content.source.buff.clone();
    let preview = self.content.preview.clone();
    let headerbar = self.header.container.clone();
    self.header.open.connect_clicked(move |_| {
        // Программировать кнопку здесь.
    });
}
#}

Создание OpenDialog

Создадим новый OpenDialog внутри connect_clicked(). Открывая диалоговое окно, мы попытаемся передать родительскую директорию в current_file, если она существует, поэтому окно для открытия файла по умолчанию использует данную директорию.

Заметьте, я здесь использую if let Some(ref path), а не просто map из-за ограничений на заимствования - если нельзя добиться того, что map будет правильно заимствовать, следует использовать match или if let.


# #![allow(unused_variables)]
#fn main() {
// Создать диалоговое окно для открытия файла, используя родительскую
// директорию в качестве предпочитаемой, если он установлен.
let open_dialog = OpenDialog::new({
    let lock = current_file.read().unwrap();
    if let Some(ref path) = *lock {
        path.get_dir()
    } else {
        None
    }
});
#}

Запуск диалогового окна

После получения переменной open_dialog, мы можем запустить диалог, а также:

  • захватить выбранный путь к файлу
  • считать данные из файла в буфер
  • обновить панель для web просмотра
  • обновить название

# #![allow(unused_variables)]
#fn main() {
// Запускает диалоговое окно и открывает файл, если он был выбран.
if let Some(new_file) = open_dialog.run() {
    if let Ok(mut file) = File::open(&new_file) {
        // Считать содержимое файла в находящийся в памяти буфер.
        let mut contents = String::new();
        let _ = file.read_to_string(&mut contents);

        // Обновить название.
        set_title(&headerbar, &new_file);
        if let Some(parent) = new_file.parent() {
            let subtitle: &str = &parent.to_string_lossy();
            headerbar.set_subtitle(subtitle);
        }

        // Установить публично доступный путь к файлу.
        *current_file.write().unwrap() =
            Some(ActiveMetadata::new(new_file, &contents.as_bytes()));

        // Обновить содержимое редактора и предпросмотровую панель.
        editor.set_text(&contents);
        preview.load_html(&render(&contents), None);
    }
}
#}

Полный код


# #![allow(unused_variables)]
#fn main() {
fn open_file(&self, current_file: Arc<RwLock<Option<ActiveMetadata>>>) {
    let editor = self.content.source.buff.clone();
    let preview = self.content.preview.clone();
    let headerbar = self.header.container.clone();
    self.header.open.connect_clicked(move |_| {
        // Создать диалоговое окно для открытия файла, используя родительскую
        // директорию в качестве предпочитаемой, если он установлен.
        let open_dialog = OpenDialog::new({
            let lock = current_file.read().unwrap();
            if let Some(ref path) = *lock {
                path.get_dir()
            } else {
                None
            }
        });

        // Запускает диалоговое окно и открывает файл, если он был выбран.
        if let Some(new_file) = open_dialog.run() {
            if let Ok(mut file) = File::open(&new_file) {
                // Считать содержимое файла в находящийся в памяти буфер.
                let mut contents = String::new();
                let _ = file.read_to_string(&mut contents);

                // Обновить название.
                set_title(&headerbar, &new_file);
                if let Some(parent) = new_file.parent() {
                    let subtitle: &str = &parent.to_string_lossy();
                    headerbar.set_subtitle(subtitle);
                }

                // Установить публично доступный путь к файлу.
                *current_file.write().unwrap() =
                    Some(ActiveMetadata::new(new_file, &contents.as_bytes()));

                // Обновить содержимое редактора и предпросмотровую панель.
                editor.set_text(&contents);
                preview.load_html(&render(&contents), None);
            }
        }
    });
}
#}