commit 1588af501b9fbed2e088fe1a10709cd2fe5c9432 Author: Able Date: Thu Jun 3 04:05:08 2021 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..df2c1e1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,185 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "able-editor" +version = "0.1.0" +dependencies = [ + "fltk", + "home", + "log", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "fltk" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93976066512905df1768cbf8bfe229089f91e992fb826b5ae0aa04db26c374ed" +dependencies = [ + "bitflags", + "fltk-derive", + "fltk-sys", + "lazy_static", + "objc", + "raw-window-handle", +] + +[[package]] +name = "fltk-derive" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59d32edd6499bef496473f31281a941c8309026a19e4a92839db3bbda591474" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "fltk-sys" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd93e3698638a72c5dd38b9cb34a9eb90417f0ef59028de0f7e3f008304754e3" +dependencies = [ + "cmake", + "libc", +] + +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" +dependencies = [ + "libc", +] + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ca57c4f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "able-editor" +version = "0.1.0" +authors = ["Able "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fltk = "*" +home = "0.5.3" +log="*" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..ccc2956 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,24 @@ +use serde::Deserialize; +use std::fs; + +#[derive(Debug, Deserialize, Copy, Clone)] +pub struct Configuration { + log: Option, +} +const DEFAULT_CONFIG: Configuration = Configuration { log: Some(true) }; +pub fn load_config() -> Configuration { + let filename = "assets/config.toml"; + + let contents = fs::read_to_string(filename); + match contents { + Ok(o) => { + println!("{}", o); + let decoded: Configuration = toml::from_str(&o).unwrap_or(DEFAULT_CONFIG); + return decoded; + } + Err(e) => { + println!("{}", e); + return DEFAULT_CONFIG; + } + } +} diff --git a/src/editor.rs b/src/editor.rs new file mode 100644 index 0000000..344fe20 --- /dev/null +++ b/src/editor.rs @@ -0,0 +1,47 @@ +use fltk::{ + enums::{CallbackTrigger, Color, Font}, + prelude::{DisplayExt, WidgetBase, WidgetExt}, + text, +}; +use std::ops::{Deref, DerefMut}; + +pub struct Editor { + editor: text::TextEditor, +} + +impl Editor { + pub fn new(buf: text::TextBuffer) -> Self { + let mut editor = text::TextEditor::new(5, 35, 790, 560, ""); + editor.set_buffer(Some(buf)); + + editor.set_text_color(Color::White); + editor.set_color(Color::Black); + editor.set_cursor_style(fltk::text::Cursor::Simple); + + #[cfg(target_os = "macos")] + editor.resize(5, 5, 790, 590); + + editor.set_scrollbar_size(15); + editor.set_text_font(Font::Screen); + editor.set_linenumber_width(32); + editor.set_linenumber_fgcolor(Color::White); + editor.set_linenumber_bgcolor(Color::Black); + editor.set_trigger(CallbackTrigger::Changed); + editor.set_cursor_color(Color::White); + Self { editor } + } +} + +impl Deref for Editor { + type Target = text::TextEditor; + + fn deref(&self) -> &Self::Target { + &self.editor + } +} + +impl DerefMut for Editor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.editor + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ba378b1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,249 @@ +use fltk::{ + app, dialog, + enums::Event, + prelude::{GroupExt, WidgetBase, WidgetExt, WindowExt}, + text, window, +}; +use std::{error, panic, path}; +mod editor; +mod menu; +#[derive(Copy, Clone)] +pub enum Message { + Changed, + New, + Open, + Save, + SaveAs, + Quit, + Cut, + Copy, + Paste, + Config, +} + +pub fn center() -> (i32, i32) { + ( + (app::screen_size().0 / 2.0) as i32, + (app::screen_size().1 / 2.0) as i32, + ) +} + +pub struct App { + app: app::App, + saved: bool, + filename: String, + r: app::Receiver, + buf: text::TextBuffer, + editor: editor::Editor, +} + +impl App { + pub fn new(args: Vec) -> Self { + let app = app::App::default().with_scheme(app::Scheme::Gtk); + app::background(0, 0, 0); + + app::foreground(255, 255, 255); + + app::set_frame_type(fltk::enums::FrameType::NoBox); + + let (s, r) = app::channel::(); + let mut buf = text::TextBuffer::default(); + buf.set_tab_distance(4); + let mut main_win = window::Window::default() + .with_size(800, 600) + .center_screen() + .with_label("AblEditor"); + let _menu = menu::Menu::new(&s); + + let mut frame = fltk::frame::Frame::default(); + frame.set_label_size(30); + frame.set_label(&"hi"); + let mut editor = editor::Editor::new(buf.clone()); + editor.emit(s, Message::Changed); + main_win.make_resizable(true); + // only resize editor, not the menu bar + main_win.resizable(&*editor); + main_win.end(); + main_win.show(); + main_win.set_callback(move |_| { + if app::event() == Event::Close { + s.send(Message::Quit); + } + }); + let filename = if args.len() > 1 { + let file = path::Path::new(&args[1]); + assert!( + file.exists() && file.is_file(), + "An error occured while opening the file!" + ); + buf.load_file(&args[1]).unwrap(); + args[1].clone() + } else { + String::new() + }; + // Handle drag and drop + let mut dnd = false; + let mut released = false; + + editor.handle({ + let mut buf = buf.clone(); + move |_, ev| match ev { + Event::DndEnter => { + dnd = true; + true + } + Event::DndDrag => true, + Event::DndRelease => { + released = true; + true + } + Event::Paste => { + if dnd && released { + let path = app::event_text(); + let path = std::path::Path::new(&path); + assert!(path.exists()); + buf.load_file(&path).unwrap(); + dnd = false; + released = false; + true + } else { + false + } + } + Event::DndLeave => { + dnd = false; + released = false; + true + } + _ => false, + } + }); + + Self { + app, + saved: true, + filename, + r, + buf, + editor, + } + } + + pub fn save_file(&mut self) -> Result<(), Box> { + let mut filename = self.filename.clone(); + if self.saved { + if filename.is_empty() { + let mut dlg = dialog::FileDialog::new(dialog::FileDialogType::BrowseSaveFile); + dlg.set_option(dialog::FileDialogOptions::SaveAsConfirm); + dlg.show(); + filename = dlg.filename().to_string_lossy().to_string(); + if !filename.is_empty() { + self.buf.save_file(&filename).unwrap_or_else(|_| { + dialog::alert(center().0 - 200, center().1 - 100, "Please specify a file!") + }); + self.saved = true; + } + } else if path::Path::new(&filename).exists() { + self.buf.save_file(&filename)?; + self.saved = true; + } else { + dialog::alert(center().0 - 200, center().1 - 100, "Please specify a file!") + } + } else { + let mut dlg = dialog::FileDialog::new(dialog::FileDialogType::BrowseSaveFile); + dlg.set_option(dialog::FileDialogOptions::SaveAsConfirm); + dlg.show(); + filename = dlg.filename().to_string_lossy().to_string(); + if !filename.is_empty() { + self.buf.save_file(&filename).unwrap_or_else(|_| { + dialog::alert(center().0 - 200, center().1 - 100, "Please specify a file!") + }); + self.saved = true; + } + } + Ok(()) + } + pub fn launch(&mut self) { + while self.app.wait() { + use Message::*; + if let Some(msg) = self.r.recv() { + match msg { + Changed => self.saved = false, + New => { + if self.buf.text() != "" { + let x = dialog::choice( + center().0 - 200, + center().1 - 100, + "File unsaved, Do you wish to continue?", + "Yes", + "No!", + "", + ); + if x == 0 { + self.buf.set_text(""); + } + } + } + Open => { + let mut dlg = dialog::FileDialog::new(dialog::FileDialogType::BrowseFile); + dlg.set_option(dialog::FileDialogOptions::NoOptions); + dlg.show(); + let filename = dlg.filename().to_string_lossy().to_string(); + if !filename.is_empty() { + if path::Path::new(&filename).exists() { + self.buf.load_file(&filename).unwrap(); + self.filename = filename; + } else { + dialog::alert( + center().0 - 200, + center().1 - 100, + "File does not exist!", + ) + } + } + } + Save | SaveAs => self.save_file().unwrap(), + + Quit => { + if self.saved { + self.app.quit(); + } else { + let x = dialog::choice( + center().0 - 200, + center().1 - 100, + "Would you like to save your work?", + "Yes", + "No", + "", + ); + + if x == 0 { + self.save_file().unwrap(); + self.app.quit(); + } else { + self.app.quit(); + } + } + } + Cut => self.editor.cut(), + Copy => self.editor.copy(), + Paste => self.editor.paste(), + _ => {} + } + } + } + } +} + +fn main() { + panic::set_hook(Box::new(|info| { + if let Some(s) = info.payload().downcast_ref::<&str>() { + dialog::message(center().0 - 200, center().1 - 100, s); + } else { + dialog::message(center().0 - 200, center().1 - 100, &info.to_string()); + } + })); + let args: Vec<_> = std::env::args().collect(); + let mut app = App::new(args); + app.launch(); +} diff --git a/src/menu.rs b/src/menu.rs new file mode 100644 index 0000000..8c54842 --- /dev/null +++ b/src/menu.rs @@ -0,0 +1,105 @@ +use fltk::{ + app, + enums::{Color, Shortcut}, + menu, + prelude::{MenuExt, WidgetExt}, +}; + +use crate::Message; +pub struct Menu { + _menu: menu::SysMenuBar, +} + +impl Menu { + pub fn new(s: &app::Sender) -> Self { + let mut menu = menu::SysMenuBar::default().with_size(800, 35); + + menu.set_text_color(Color::White); + menu.set_color(Color::Black); + menu.add_emit( + "&File/New...\t", + Shortcut::Ctrl | 'n', + menu::MenuFlag::Normal, + *s, + Message::New, + ); + + menu.add_emit( + "&File/Open...\t", + Shortcut::Ctrl | 'o', + menu::MenuFlag::Normal, + *s, + Message::Open, + ); + + menu.add_emit( + "&File/Save\t", + Shortcut::Ctrl | 's', + menu::MenuFlag::Normal, + *s, + Message::Save, + ); + + menu.add_emit( + "&File/Save as...\t", + Shortcut::Ctrl | 'w', + menu::MenuFlag::Normal, + *s, + Message::SaveAs, + ); + + menu.add_emit( + "&File/Quit\t", + Shortcut::Ctrl | 'q', + menu::MenuFlag::Normal, + *s, + Message::Quit, + ); + + menu.add_emit( + "&Edit/Cut\t", + Shortcut::Ctrl | 'x', + menu::MenuFlag::Normal, + *s, + Message::Cut, + ); + + menu.add_emit( + "&Edit/Copy\t", + Shortcut::Ctrl | 'c', + menu::MenuFlag::Normal, + *s, + Message::Copy, + ); + + menu.add_emit( + "&Edit/Paste\t", + Shortcut::Ctrl | 'v', + menu::MenuFlag::Normal, + *s, + Message::Paste, + ); + + /* + menu.add_emit( + "&Tools/Config\t", + Shortcut::Ctrl | '/', + menu::MenuFlag::Normal, + *s, + Message::Config, + ); + menu.add_emit( + "&Tools/Terminal\t", + Shortcut::Ctrl | 't', + menu::MenuFlag::Normal, + *s, + Message::Config, + ); + */ + if let Some(mut item) = menu.find_item("&File/Quit\t") { + item.set_label_color(Color::Red); + } + + Self { _menu: menu } + } +}