From 6144feb8571a10a53dce79965a3a59262f7eec7c Mon Sep 17 00:00:00 2001 From: Nefomemes Date: Thu, 28 Oct 2021 17:15:59 +0700 Subject: [PATCH] refactor: move editor code to a separate EchidnaCoreEditor widget This allows for a more component-based codebase, like those in web development. Also added barebone minimap and refactored the file loading. Should have commited them in a separate commit, but I'm just used to overusing git add -p. --- src/components/editor/editor.ui | 29 +++++++++++ src/components/editor/imp.rs | 37 ++++++++++++++ src/components/editor/mod.rs | 85 +++++++++++++++++++++++++++++++++ src/components/mod.rs | 1 + src/components/window/file.rs | 78 ++++++++---------------------- 5 files changed, 173 insertions(+), 57 deletions(-) create mode 100644 src/components/editor/editor.ui create mode 100644 src/components/editor/imp.rs create mode 100644 src/components/editor/mod.rs diff --git a/src/components/editor/editor.ui b/src/components/editor/editor.ui new file mode 100644 index 0000000..2f48cdc --- /dev/null +++ b/src/components/editor/editor.ui @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/src/components/editor/imp.rs b/src/components/editor/imp.rs new file mode 100644 index 0000000..fc37cc2 --- /dev/null +++ b/src/components/editor/imp.rs @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + + use gtk::prelude::*; + use gtk::subclass::prelude::*; + use gtk::CompositeTemplate; + + #[derive(Default, CompositeTemplate)] + #[template(file = "./editor.ui")] + pub struct EchidnaCoreEditor { + #[template_child] + pub minimap: TemplateChild, + #[template_child] + pub sourceview: TemplateChild + +} + + #[glib::object_subclass] + impl ObjectSubclass for EchidnaCoreEditor { + const NAME: &'static str = "EchidnaCoreEditor"; + type Type = super::EchidnaCoreEditor; + type ParentType = gtk::Box; + + fn class_init(class: &mut Self::Class) { + Self::bind_template(class); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for EchidnaCoreEditor {} + impl WidgetImpl for EchidnaCoreEditor {} + impl BoxImpl for EchidnaCoreEditor {} + \ No newline at end of file diff --git a/src/components/editor/mod.rs b/src/components/editor/mod.rs new file mode 100644 index 0000000..8b18912 --- /dev/null +++ b/src/components/editor/mod.rs @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +pub mod imp; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use sourceview::{prelude::*, Buffer, FileExt as SourceFileExt, FileLoader, LanguageManager}; + +glib::wrapper! { + pub struct EchidnaCoreEditor(ObjectSubclass) + @extends gtk::Box, gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; +} + + + +impl EchidnaCoreEditor { + + + pub fn new>(file: Option<&P>) -> Self { + let this: Self = + glib::Object::new(&[]).expect("Failed to create 'EchidnaCoreEditor' component."); + let this_imp = this.to_imp(); + // Without cloning it, for some reasons the Rust compiler complains about &this.to_imp().sourceview not being IsA + this_imp + .minimap + .set_view(&this_imp.sourceview.clone()); + + if file.is_some() { + let file = file.unwrap(); + + let file_location = file + .location() + .expect("file is required to have a location"); + let cancellable = gio::Cancellable::new(); + let filepath = file_location.path().expect("No filepath"); + let file_info_result = + file_location.query_info("*", gio::FileQueryInfoFlags::NONE, Some(&cancellable)); + match file_info_result { + Ok(info) => match info.content_type() { + Some(content_type) => { + println!( + "Opened {} and found its content type is {}.", + "file", + content_type.to_string() + ); + let buffer = this_imp.sourceview.buffer().downcast::().expect("Cannot downcast the sourceview's buffer. Maybe the sourceview's buffer is not IsA."); + let language_manager = LanguageManager::new(); + let language = language_manager.guess_language(Some(&info.name().to_str().expect("Could not open the file because its name is not supported by Unicode.")), None); + + + match language { + Some(lang) => buffer.set_language(Some(&lang)), + None => {} + } + + let file_loader: FileLoader = FileLoader::new(&buffer, file); + + file_loader.load_async( + glib::Priority::default(), + Some(&cancellable), + |_, _| {}, + |result| { + if result.is_err() { + panic!(result.err()); + } + }, + ); + } + None => println!("It does not seem like {:?} has a type", filepath), + }, + Err(e) => println!( + "Could not retrieve file information for {:?} because:\n{}", + filepath, e + ), + } + } + this + } + + pub fn to_imp(&self) -> &imp::EchidnaCoreEditor { + imp::EchidnaCoreEditor::from_instance(self) + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index d7f7fbe..0317bce 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -5,3 +5,4 @@ pub mod app; pub mod sidebar; pub mod window; +pub mod editor; \ No newline at end of file diff --git a/src/components/window/file.rs b/src/components/window/file.rs index 1d73e2a..84f8568 100644 --- a/src/components/window/file.rs +++ b/src/components/window/file.rs @@ -2,12 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use gio::Cancellable; +use crate::components::editor::EchidnaCoreEditor; +// use gio::Cancellable; use glib::clone; use gtk::{ prelude::*, subclass::prelude::*, FileChooserAction, FileChooserDialog, Label, ResponseType, }; -use sourceview::{prelude::*, Buffer, LanguageManager, View}; +use sourceview::File; pub trait FileImplementedEditor { fn action_open_file(&self); @@ -49,65 +50,28 @@ impl FileImplementedEditor for super::EchidnaWindow { dialog.connect_response(clone!( @weak self as window, => move |dialog, response| { if response == ResponseType::Accept { - let file_option = dialog.file(); - match file_option { - Some(file) => { - dialog.destroy(); - Self::open_file(&super::imp::EchidnaWindow::from_instance(&window).notebook, file); - }, - None => {}, - } + let file = dialog.file().expect(""); + Self::open_file(&super::imp::EchidnaWindow::from_instance(&window).notebook, file); + } else if response == ResponseType::Cancel { dialog.destroy(); } })); } - fn open_file(notebook: >k::Notebook, file: gio::File) { - let cancellable = Cancellable::new(); - let filepath = file.path().expect("No filepath"); - let file_info_result = - file.query_info("*", gio::FileQueryInfoFlags::NONE, Some(&cancellable)); - match file_info_result { - Ok(info) => match info.content_type() { - Some(content_type) => { - println!( - "Opened {} and found its content type is {}.", - "file", - content_type.to_string() - ); - let content_cancellable = Cancellable::new(); - let file_content = file.load_contents(Some(&content_cancellable)); - match file_content { - Ok(content) => { - let (int_vec, _text_option) = content; - let language_manager = LanguageManager::new(); - let language = language_manager.guess_language(Some(&info.name().to_str().expect("Could not open the file because its name is not supported by Unicode.")), None); - - let buffer = Buffer::new(None); - buffer.set_text(std::str::from_utf8(&int_vec).expect(format!("Could not parse the contents of {:?} as it's unsupported by UTF-8", filepath).as_str())); - match language { - Some(lang) => buffer.set_language(Some(&lang)), - None => {} - } - - let sourceview = View::with_buffer(&buffer); - - notebook.prepend_page( - &sourceview, - Some(&Label::new(Some( - &info.name().to_str().expect("Could not parse file's name"), - ))), - ); - } - Err(e) => println!("Could not open {:?} because:\n{:#?}", filepath, e), - } - } - None => println!("It does not seem like {:?} has a type", filepath), - }, - Err(e) => println!( - "Could not retrieve file information for {:?} because:\n{}", - filepath, e - ), - } + fn open_file(notebook: >k::Notebook, file_location: gio::File) { + let file = File::builder().location(&file_location).build(); + let editor_page = EchidnaCoreEditor::new(Some(&file)); + notebook.prepend_page( + &editor_page, + Some(&Label::new(Some( + &file_location + .path() + .expect("The file's path is missing") + .file_name() + .expect("Could not get the file name, as it ends with ..") + .to_str() + .expect("Could not parse the file name, as it is not a valid Unicode."), + ))), + ); } }