diff --git a/src/app/imp.rs b/src/app/imp.rs deleted file mode 100644 index d4643f8..0000000 --- a/src/app/imp.rs +++ /dev/null @@ -1,198 +0,0 @@ -/* 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 std::str::from_utf8; -use sourceview::{View, Buffer, LanguageManager, }; -use sourceview::prelude::*; -use gtk::subclass::prelude::*; -use gtk::prelude::*; -use super::menubar::MenubarImplementedEditor; -use gtk::{ - ApplicationWindow, - Application, - FileChooserDialog, - FileChooserAction, - ResponseType, - Label - -}; - -use gio::{ - MenuModel, - SimpleAction, - Cancellable -}; -//use super::workspace; -use glib::clone; - -#[derive(Debug, Default)] -pub struct EchidnaEditor { - pub name: &'static str, - pub app_id: &'static str -} - -pub trait EchidnaEditorExt { - fn action_open_file(window: ApplicationWindow, app: super::EchidnaEditor, action: &SimpleAction, variant: Option<&glib::Variant>, notebook: gtk::Notebook); - fn open_file(notebook: >k::Notebook, file: gio::File); -} - - - -#[glib::object_subclass] -impl ObjectSubclass for EchidnaEditor { - const NAME: &'static str = "EchidnaEditorApplication"; - type Type = super::EchidnaEditor; - type ParentType = Application; - - fn new() -> Self { - Self { - name: "Echidna Code Editor", - app_id: "land.echidna.editor" - } - } -} - - - -impl EchidnaEditorExt for EchidnaEditor { - - /* - Open a file and put it in an editor and the opened files bar. - - - Open a file chooser dialog. - - Connect a signal to it and get the file choosen. - - Read th file's information and - - TODO: if it's a plain text, - - TODO: Load the file's content - - TODO: Create an editor - - TODO: Set the editor's content to the file's content. - - TODO: Somehow keep track of what file belongs to what editor/opened file bar widget. - - TODO: If the user enables Autosave, listen to the editor's changes and automatically save the editor's content. - - TODO: Close the editor and the tab widget when the file is closed (triggered by the X button on them). - - Perhaps some of the last points should not be implemented in this function but rather in another function that keeps track of every files. - */ - fn action_open_file(window: ApplicationWindow, app: super::EchidnaEditor, _action: &SimpleAction, variant: Option<&glib::Variant>, notebook: gtk::Notebook){ - - let dialog: FileChooserDialog = FileChooserDialog::new(Some("Open a file"), - Some(&window), - FileChooserAction::Open, - &[("Cancel", ResponseType::Cancel), - ("Open", ResponseType::Accept) ]); - - - dialog.set_visible(true); - - - - // TODO: Somehow inserts self to this function. - // This function sets the callback function as 'static, which for some reasons ban cloning self into it. Idk why. - dialog.connect_response(clone!( @weak app, @weak window, => - move |dialog, response| { - - if response == ResponseType::Accept { - - let file_option = dialog.file(); - - match file_option { - Some(file) => { - dialog.destroy(); - - Self::open_file(¬ebook, file); - - }, - None => { - - }, - } - } 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(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), - } - - - } - - -} - -impl ObjectImpl for EchidnaEditor { - - - -} - -impl ApplicationImpl for EchidnaEditor { - - fn activate(&self, app: &Self::Type){ - - let builder = gtk::Builder::from_string(include_str!("../../ui/window.ui")); - let window: ApplicationWindow = builder - .object("window") - .expect("Could not get object 'window' from builder."); - - let menubuilder = gtk::Builder::from_string(include_str!("../../ui/menu.ui")); - let menubar: MenuModel = menubuilder - .object("menu") - .expect("Could not get object 'menu' from builder."); - - self.setup_menubar(app, &window, &builder.clone()); - - window.set_application(Some(app)); - - window.present(); - - } - -} - -impl GtkApplicationImpl for EchidnaEditor {} - diff --git a/src/components/app/imp.rs b/src/components/app/imp.rs new file mode 100644 index 0000000..9dd1c98 --- /dev/null +++ b/src/components/app/imp.rs @@ -0,0 +1,60 @@ +/* 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 super::super::window::EchidnaWindow; +use super::super::window::menubar::MenubarImplementedEditor; + +use gtk::subclass::prelude::*; +use gtk::prelude::*; + +use gtk::{ + Application, +}; + +#[derive(Debug, Default)] +pub struct EchidnaEditor { + pub name: &'static str, + pub app_id: &'static str +} + + + + +#[glib::object_subclass] +impl ObjectSubclass for EchidnaEditor { + const NAME: &'static str = "EchidnaEditorApplication"; + type Type = super::EchidnaEditor; + type ParentType = Application; + + fn new() -> Self { + Self { + name: "Echidna Code Editor", + app_id: "land.echidna.editor" + } + } +} + + +impl ObjectImpl for EchidnaEditor { + + + +} + +impl ApplicationImpl for EchidnaEditor { + + fn activate(&self, app: &Self::Type){ + + let window = EchidnaWindow::new(app); + + window.setup_menubar(); + window.set_application(Some(app)); + + window.present(); + + } + +} + +impl GtkApplicationImpl for EchidnaEditor {} + diff --git a/src/app/mod.rs b/src/components/app/mod.rs similarity index 96% rename from src/app/mod.rs rename to src/components/app/mod.rs index b98bcfb..8b3c9c1 100644 --- a/src/app/mod.rs +++ b/src/components/app/mod.rs @@ -3,12 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ pub mod imp; -pub mod menubar; -pub mod workspace; + use glib::wrapper; wrapper! { pub struct EchidnaEditor(ObjectSubclass) @extends gio::Application, gtk::Application, @implements gio::ActionGroup, gio::ActionMap; + } impl Default for EchidnaEditor { diff --git a/src/components/mod.rs b/src/components/mod.rs index 7a10ed2..24e9074 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -3,3 +3,4 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ pub mod app; +pub mod window; diff --git a/src/components/window/file.rs b/src/components/window/file.rs new file mode 100644 index 0000000..b043344 --- /dev/null +++ b/src/components/window/file.rs @@ -0,0 +1,134 @@ + +use glib::{ + clone +}; +use sourceview::{ + View, + Buffer, + LanguageManager, + prelude::* +}; +use gio::{ + Cancellable + +}; +use gtk::{ + FileChooserDialog, + FileChooserAction, + ResponseType, + Label, + prelude::*, + subclass::prelude::* +}; + +pub trait FileImplementedEditor { + fn action_open_file/*>*/(window: Self); + fn open_file(notebook: >k::Notebook, file: gio::File); +} + +impl FileImplementedEditor for super::EchidnaWindow { + /* + Open a file and put it in an editor and the opened files bar. + + - Open a file chooser dialog. + - Connect a signal to it and get the file choosen. + - Read th file's information and + - TODO: if it's a plain text, + - TODO: Load the file's content + - TODO: Create an editor + - TODO: Set the editor's content to the file's content. + - TODO: Somehow keep track of what file belongs to what editor/opened file bar widget. + - TODO: If the user enables Autosave, listen to the editor's changes and automatically save the editor's content. + - TODO: Close the editor and the tab widget when the file is closed (triggered by the X button on them). + + Perhaps some of the last points should not be implemented in this function but rather in another function that keeps track of every files. + */ + fn action_open_file/*>*/(window: Self){ + + let dialog: FileChooserDialog = FileChooserDialog::new(Some("Open a file"), + Some(&window), + FileChooserAction::Open, + &[("Cancel", ResponseType::Cancel), + ("Open", ResponseType::Accept) ]); + + + dialog.set_visible(true); + + + + // TODO: Somehow inserts self to this function. + // This function sets the callback function as 'static, which for some reasons ban cloning self into it. Idk why. + dialog.connect_response(clone!( @weak 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 => { + + }, + } + } 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), + } + + + } + +} \ No newline at end of file diff --git a/src/components/window/imp.rs b/src/components/window/imp.rs new file mode 100644 index 0000000..38344b2 --- /dev/null +++ b/src/components/window/imp.rs @@ -0,0 +1,37 @@ + +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::CompositeTemplate; + +#[derive(Debug, Default, CompositeTemplate)] +#[template(file = "./window.ui")] +pub struct EchidnaWindow { + #[template_child] + pub notebook: TemplateChild, +} + +#[glib::object_subclass] +impl ObjectSubclass for EchidnaWindow { + const NAME: &'static str = "EchidnaWindow"; + type Type = super::EchidnaWindow; + type ParentType = gtk::ApplicationWindow; + + + fn class_init(class: &mut Self::Class) { + Self::bind_template(class); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } +} + +impl ObjectImpl for EchidnaWindow {} + +impl WidgetImpl for EchidnaWindow {} + +impl WindowImpl for EchidnaWindow {} + +impl ApplicationWindowImpl for EchidnaWindow {} + +impl BuildableImpl for EchidnaWindow {} diff --git a/src/app/menubar.rs b/src/components/window/menubar.rs similarity index 75% rename from src/app/menubar.rs rename to src/components/window/menubar.rs index e8da0fb..7cb1924 100644 --- a/src/app/menubar.rs +++ b/src/components/window/menubar.rs @@ -1,32 +1,25 @@ -use super::imp::EchidnaEditor; -use super::imp::EchidnaEditorExt; +use super::file::FileImplementedEditor; +use super::EchidnaWindow; use gio::{MenuModel, SimpleAction}; use glib::clone; use gtk::prelude::*; use gtk::AboutDialog; pub trait MenubarImplementedEditor { - fn setup_menubar( - &self, - app: &super::EchidnaEditor, - window: >k::ApplicationWindow, - builder: >k::Builder, - ); + fn setup_menubar(&self); } -impl MenubarImplementedEditor for EchidnaEditor { - fn setup_menubar( - &self, - app: &super::EchidnaEditor, - window: >k::ApplicationWindow, - builder: >k::Builder, - ) { - let menubuilder = gtk::Builder::from_string(include_str!("../../ui/menu.ui")); +impl MenubarImplementedEditor for EchidnaWindow { + fn setup_menubar(&self) { + let app = &self + .application() + .expect("&self does not have an application set."); + let menubuilder = gtk::Builder::from_string(include_str!("../../../ui/menu.ui")); let menubar: MenuModel = menubuilder .object("menu") .expect("Could not get object 'menu' from builder."); app.set_menubar(Some(&menubar)); - window.set_show_menubar(true); + &self.set_show_menubar(true); let act_exit: SimpleAction = SimpleAction::new("exit", None); app.add_action(&act_exit); @@ -49,9 +42,6 @@ impl MenubarImplementedEditor for EchidnaEditor { about_dialog.set_visible(true); }); - let notebook: gtk::Notebook = builder - .object("echidna-notebook") - .expect("Could not get 'echidna-notebook' from builder."); //app.notebook = Some(Rc::new(RefCell::new(notebook))); let act_exit: SimpleAction = SimpleAction::new("exit", None); app.add_action(&act_exit); @@ -92,20 +82,22 @@ impl MenubarImplementedEditor for EchidnaEditor { let act_window_close = SimpleAction::new("close", None); - window.add_action(&act_window_close); + &self.add_action(&act_window_close); + + { + let window = self.clone(); - act_window_close.connect_activate(clone!(@weak window => - move | _action, _variant | { + act_window_close.connect_activate(move |_action, _variant| { window.close(); - } - )); + }); + } let action_open_file: SimpleAction = SimpleAction::new("open-file", None); - window.add_action(&action_open_file); - action_open_file.connect_activate(clone!(@weak window, @weak app, @weak notebook => + &self.add_action(&action_open_file); + action_open_file.connect_activate(clone!(@weak self as window => move |action, variant| { - Self::action_open_file(window, app, action, variant, notebook); + Self::action_open_file(window); })); } } diff --git a/src/components/window/mod.rs b/src/components/window/mod.rs new file mode 100644 index 0000000..bff0d6f --- /dev/null +++ b/src/components/window/mod.rs @@ -0,0 +1,36 @@ +mod file; + +mod imp; +pub mod menubar; + +use glib::{ + + + object::IsA, + +}; + +glib::wrapper! { + pub struct EchidnaWindow(ObjectSubclass) + @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, + @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager; +} + +impl EchidnaWindow { + pub fn new>(application: &P) -> Self { + + let object = glib::Object::new(&[ + ("application", &application) + ]); + + match object { + Ok(o) => o, + Err(e) => panic!("Error in making EchidnaApplication {}", e), + } + } + + + + +} + diff --git a/src/components/window/sidebar.rs b/src/components/window/sidebar.rs new file mode 100644 index 0000000..4014aed --- /dev/null +++ b/src/components/window/sidebar.rs @@ -0,0 +1,9 @@ +trait SidebarImplementedEditor { + fn setup_sidebar(); +} + +impl SidebarImplementedEditor for super::imp::EchidnaEditor { + fn setup_sidebar(){ + + } +} \ No newline at end of file diff --git a/src/components/window/window.ui b/src/components/window/window.ui new file mode 100644 index 0000000..f8ec6b6 --- /dev/null +++ b/src/components/window/window.ui @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/src/app/workspace.rs b/src/components/window/workspace.rs similarity index 100% rename from src/app/workspace.rs rename to src/components/window/workspace.rs diff --git a/src/main.rs b/src/main.rs index 5bb4936..01d237c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,14 @@ * 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/. */ -mod app; +mod components; -use gtk::prelude::ApplicationExtManual; use app::EchidnaEditor; +use components::app; +use gtk::prelude::ApplicationExtManual; fn main() { - let app = EchidnaEditor::new( - "land.echidna.editor", - ); + let app = EchidnaEditor::new("land.echidna.editor"); std::process::exit(app.run()); -} \ No newline at end of file +}