added item window, and item movement

This commit is contained in:
Penwing 2024-02-01 20:46:22 +01:00
parent 34274dfb9d
commit afec8f6426
7 changed files with 887 additions and 741 deletions

View file

@ -15,334 +15,345 @@ use crate::TERMINAL_RANGE;
use editor::{CodeEditor, Syntax}; use editor::{CodeEditor, Syntax};
impl Calcifer { impl Calcifer {
pub fn draw_settings(&mut self, ctx: &egui::Context) { pub fn draw_settings(&mut self, ctx: &egui::Context) {
egui::SidePanel::left("settings") egui::SidePanel::left("settings")
.resizable(false) .resizable(false)
.exact_width(self.font_size * 1.8) .exact_width(self.font_size * 1.8)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.vertical(|ui| { ui.vertical(|ui| {
if ui.add(egui::Button::new("📁")).clicked() { if ui.add(egui::Button::new("📁")).clicked() {
if let Some(path) = rfd::FileDialog::new() if let Some(path) = rfd::FileDialog::new()
.set_directory(self.home.as_path()) .set_directory(self.home.as_path())
.pick_file() .pick_file()
{ {
self.open_file(Some(&path)); self.open_file(Some(&path));
} }
} }
ui.separator(); ui.separator();
self.tree_visible = self.toggle(ui, self.tree_visible, "📦"); self.tree_visible = self.toggle(ui, self.tree_visible, "📦");
ui.separator(); ui.separator();
self.terminal_visible = self.toggle(ui, self.terminal_visible, "🖵"); self.terminal_visible = self.toggle(ui, self.terminal_visible, "🖵");
ui.separator(); ui.separator();
self.search_menu.visible = self.toggle(ui, self.search_menu.visible, "🔍"); self.search_menu.visible = self.toggle(ui, self.search_menu.visible, "🔍");
ui.separator(); ui.separator();
self.settings_menu.visible = self.toggle(ui, self.settings_menu.visible, ""); self.settings_menu.visible = self.toggle(ui, self.settings_menu.visible, "");
ui.separator(); ui.separator();
self.shortcuts_menu.visible = self.toggle(ui, self.shortcuts_menu.visible, ""); self.shortcuts_menu.visible = self.toggle(ui, self.shortcuts_menu.visible, "");
ui.separator(); ui.separator();
self.profiler_visible = self.toggle(ui, self.profiler_visible, ""); self.profiler_visible = self.toggle(ui, self.profiler_visible, "");
if self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION { // if self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION {
ui.separator(); // ui.separator();
self.project_mode = self.toggle(ui, self.project_mode, "c"); // self.project_mode = self.toggle(ui, self.project_mode, "c");
} // }
}); });
}); });
} }
pub fn draw_tree_panel(&mut self, ctx: &egui::Context) { pub fn draw_tree_panel(&mut self, ctx: &egui::Context) {
if !self.tree_visible { if !self.tree_visible {
return; return;
} }
if self.file_tree.is_none() { if self.file_tree.is_none() {
self.file_tree = Some(panels::generate_folder_entry(self.home.as_path())); self.file_tree = Some(panels::generate_folder_entry(self.home.as_path()));
} }
let mut n_files: usize = 0; let mut n_files: usize = 0;
egui::SidePanel::left("file_tree_panel").show(ctx, |ui| { egui::SidePanel::left("file_tree_panel").show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Bookshelf "); ui.label("Bookshelf ");
}); });
ui.separator(); ui.separator();
ui.label(format!("{} files displayed", self.n_file_displayed)); ui.label(format!("{} files displayed", self.n_file_displayed));
ui.separator(); ui.separator();
egui::ScrollArea::vertical().show(ui, |ui| { egui::ScrollArea::vertical().show(ui, |ui| {
if let Some(file_tree) = self.file_tree.clone() { if let Some(file_tree) = self.file_tree.clone() {
let update_requested = self.list_files(ui, &file_tree, &mut n_files); let update_requested = self.list_files(ui, &file_tree, &mut n_files);
if update_requested { if update_requested {
self.file_tree = Some(panels::update_file_tree( self.file_tree = Some(panels::update_file_tree(
file_tree, file_tree,
self.tree_dir_opened.clone(), self.tree_dir_opened.clone(),
)); ));
} }
} else { } else {
ui.label("No book on the Bookshelf"); ui.label("No book on the Bookshelf");
} }
ui.separator(); ui.separator();
}); });
}); });
self.n_file_displayed = n_files.clone(); self.n_file_displayed = n_files.clone();
} }
pub fn draw_bottom_tray(&mut self, ctx: &egui::Context) { pub fn draw_bottom_tray(&mut self, ctx: &egui::Context) {
egui::TopBottomPanel::bottom("tray") egui::TopBottomPanel::bottom("tray")
.default_height(self.font_size * 1.2) .default_height(self.font_size * 1.2)
.resizable(false) .resizable(false)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.label(self.profiler()); ui.label(self.profiler());
}); });
} }
pub fn draw_terminal_panel(&mut self, ctx: &egui::Context) { pub fn draw_terminal_panel(&mut self, ctx: &egui::Context) {
if !self.terminal_visible { if !self.terminal_visible {
return; return;
} }
egui::TopBottomPanel::bottom("terminal") egui::TopBottomPanel::bottom("terminal")
.default_height(TERMINAL_HEIGHT) .default_height(TERMINAL_HEIGHT)
.height_range(Rangef::new(TERMINAL_RANGE.start, TERMINAL_RANGE.end)) .height_range(Rangef::new(TERMINAL_RANGE.start, TERMINAL_RANGE.end))
.resizable(true) .resizable(true)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
let command_color = core::hex_str_to_color(self.theme.functions); let command_color = core::hex_str_to_color(self.theme.functions);
let entry_color = core::hex_str_to_color(self.theme.literals); let entry_color = core::hex_str_to_color(self.theme.literals);
let bg_color = core::hex_str_to_color(self.theme.bg); let bg_color = core::hex_str_to_color(self.theme.bg);
ui.label(""); ui.label("");
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.add(egui::Button::new("")).clicked() { if ui.add(egui::Button::new("")).clicked() {
self.command_history.retain(|e| !e.finished); self.command_history.retain(|e| !e.finished);
} }
ui.style_mut().visuals.extreme_bg_color = bg_color; ui.style_mut().visuals.extreme_bg_color = bg_color;
let Self { command, .. } = self; let Self { command, .. } = self;
ui.colored_label( ui.colored_label(
command_color, command_color,
format_path(&env::current_dir().unwrap_or_else(|_| PathBuf::from("/"))), format_path(&env::current_dir().unwrap_or_else(|_| PathBuf::from("/"))),
); );
let response = ui.add( let response = ui.add(
egui::TextEdit::singleline(command) egui::TextEdit::singleline(command)
.desired_width(f32::INFINITY) .desired_width(f32::INFINITY)
.lock_focus(true), .lock_focus(true),
); );
if response.lost_focus() && ctx.input(|i| i.key_pressed(egui::Key::Enter)) { if response.lost_focus() && ctx.input(|i| i.key_pressed(egui::Key::Enter)) {
self.command_history self.command_history
.push(panels::send_command(self.command.clone())); .push(panels::send_command(self.command.clone()));
self.command = "".into(); self.command = "".into();
response.request_focus(); response.request_focus();
} }
}); });
ui.separator(); ui.separator();
egui::ScrollArea::vertical() egui::ScrollArea::vertical()
.stick_to_bottom(true) .stick_to_bottom(true)
.show(ui, |ui| { .show(ui, |ui| {
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
ui.separator(); ui.separator();
ui.spacing_mut().item_spacing.y = 0.0; ui.spacing_mut().item_spacing.y = 0.0;
ui.style_mut().visuals.hyperlink_color = ui.style_mut().visuals.hyperlink_color =
core::hex_str_to_color(self.theme.keywords); core::hex_str_to_color(self.theme.keywords);
for entry in &mut self.command_history { for entry in &mut self.command_history {
ui.label(""); ui.label("");
ui.horizontal(|ui| { ui.horizontal(|ui| {
if !entry.finished { if !entry.finished {
entry.update(); entry.update();
let _ = ui.link("(⌛)"); let _ = ui.link("(⌛)");
} else if ui.link("(🗐)").clicked() { } else if ui.link("(🗐)").clicked() {
entry.copy_error_code(); entry.copy_error_code();
} }
ui.colored_label( ui.colored_label(
command_color, command_color,
format!("{} {}", entry.env, entry.command), format!("{} {}", entry.env, entry.command),
); );
}); });
for line in &entry.result { for line in &entry.result {
let color = if line.error { RED } else { entry_color }; let color = if line.error { RED } else { entry_color };
ui.label(egui::RichText::new(&line.text).monospace().color(color)); ui.label(
} egui::RichText::new(&line.text)
} .monospace()
}); .color(color),
}); );
}); }
}); }
} });
});
});
});
}
pub fn draw_tab_panel(&mut self, ctx: &egui::Context) { pub fn draw_tab_panel(&mut self, ctx: &egui::Context) {
egui::TopBottomPanel::top("tabs") egui::TopBottomPanel::top("tabs")
.resizable(false) .resizable(false)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.style_mut().visuals.selection.bg_fill = ui.style_mut().visuals.selection.bg_fill =
core::hex_str_to_color(self.theme.functions); core::hex_str_to_color(self.theme.functions);
ui.style_mut().visuals.hyperlink_color = ui.style_mut().visuals.hyperlink_color =
core::hex_str_to_color(self.theme.functions); core::hex_str_to_color(self.theme.functions);
for (index, tab) in self.tabs.clone().iter().enumerate() { for (index, tab) in self.tabs.clone().iter().enumerate() {
let mut title = tab.get_name(); let mut title = tab.get_name();
if !tab.saved { if !tab.saved {
title += " ~"; title += " ~";
} }
if self.selected_tab == panels::TabNumber::from_index(index) { if self.selected_tab == panels::TabNumber::from_index(index) {
ui.style_mut().visuals.override_text_color = ui.style_mut().visuals.override_text_color =
Some(core::hex_str_to_color(self.theme.bg)); Some(core::hex_str_to_color(self.theme.bg));
} }
ui.selectable_value( ui.selectable_value(
&mut self.selected_tab, &mut self.selected_tab,
panels::TabNumber::from_index(index), panels::TabNumber::from_index(index),
title, title,
); );
ui.style_mut().visuals.override_text_color = None; ui.style_mut().visuals.override_text_color = None;
if ui.link("X").clicked() && !self.close_tab_confirm.visible { if ui.link("X").clicked() && !self.close_tab_confirm.visible {
if self.tabs.len() > 1 { if self.tabs.len() > 1 {
if tab.saved { if tab.saved {
self.delete_tab(index); self.delete_tab(index);
} else { } else {
self.close_tab_confirm.ask(); self.close_tab_confirm.ask();
self.tab_to_close = index; self.tab_to_close = index;
} }
} else { } else {
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close); egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close);
} }
} }
ui.separator(); ui.separator();
} }
if self.tabs.len() < MAX_TABS { if self.tabs.len() < MAX_TABS {
ui.selectable_value(&mut self.selected_tab, panels::TabNumber::Open, "+"); ui.selectable_value(&mut self.selected_tab, panels::TabNumber::Open, "+");
} }
if self.selected_tab == panels::TabNumber::Open { if self.selected_tab == panels::TabNumber::Open {
self.open_file(None); self.open_file(None);
} }
}); });
}); });
} }
pub fn draw_content_panel(&mut self, ctx: &egui::Context) { pub fn draw_content_panel(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui if ui
.add(egui::Button::new("open directory in terminal")) .add(egui::Button::new("open directory in terminal"))
.clicked() .clicked()
{ {
let mut path = self.tabs[self.selected_tab.to_index()].path.clone(); let mut path = self.tabs[self.selected_tab.to_index()].path.clone();
path.pop(); path.pop();
panels::send_command(format!("cd {}", path.display())); panels::send_command(format!("cd {}", path.display()));
} }
ui.label("Picked file:"); ui.label("Picked file:");
ui.monospace( ui.monospace(
self.tabs[self.selected_tab.to_index()] self.tabs[self.selected_tab.to_index()]
.path .path
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
); );
}); });
ui.separator(); ui.separator();
if self.project_mode if self.project_mode
&& self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION && self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION
{ {
self.draw_project_file(ui); self.draw_project_file(ui);
} else { } else {
self.draw_code_file(ui); self.draw_code_file(ui);
} }
}); });
} }
fn draw_code_file(&mut self, ui: &mut egui::Ui) { fn draw_code_file(&mut self, ui: &mut egui::Ui) {
let current_tab = &mut self.tabs[self.selected_tab.to_index()]; let current_tab = &mut self.tabs[self.selected_tab.to_index()];
let lines = current_tab.code.chars().filter(|&c| c == '\n').count() + 1; let lines = current_tab.code.chars().filter(|&c| c == '\n').count() + 1;
let mut override_cursor: Option<CCursorRange> = None; let mut override_cursor: Option<CCursorRange> = None;
if !self.search_menu.result_selected { if !self.search_menu.result_selected {
override_cursor = Some(CCursorRange::two( override_cursor = Some(CCursorRange::two(
CCursor::new(self.search_menu.get_cursor_start()), CCursor::new(self.search_menu.get_cursor_start()),
CCursor::new(self.search_menu.get_cursor_end()), CCursor::new(self.search_menu.get_cursor_end()),
)); ));
self.search_menu.result_selected = true; self.search_menu.result_selected = true;
} }
CodeEditor::default() CodeEditor::default()
.id_source("code editor") .id_source("code editor")
.with_rows(max(45, lines)) .with_rows(max(45, lines))
.with_fontsize(self.font_size) .with_fontsize(self.font_size)
.with_theme(self.theme) .with_theme(self.theme)
.with_syntax(to_syntax(&current_tab.language)) .with_syntax(to_syntax(&current_tab.language))
.with_numlines(true) .with_numlines(true)
.show( .show(
ui, ui,
&mut current_tab.code, &mut current_tab.code,
&mut current_tab.saved, &mut current_tab.saved,
&mut current_tab.last_cursor, &mut current_tab.last_cursor,
&mut current_tab.scroll_offset, &mut current_tab.scroll_offset,
override_cursor, override_cursor,
); );
} }
fn draw_project_file(&mut self, ui: &mut egui::Ui) { fn draw_project_file(&mut self, ui: &mut egui::Ui) {
panels::draw_project(ui, self.theme.clone(), &mut self.project_content); panels::draw_project(ui, self.theme.clone(), &mut self.project_content);
} }
pub fn draw_windows(&mut self, ctx: &egui::Context) { pub fn draw_windows(&mut self, ctx: &egui::Context) {
if self.search_menu.visible { if self.project_content.item_window.visible {
self.search_menu self.project_content.item_window.show(
.show(ctx, &mut self.tabs, &mut self.selected_tab); ctx,
} &mut self.project_content.categories[self.project_content.selected_item.category]
if self.close_tab_confirm.visible { .content[self.project_content.selected_item.row],
self.close_tab_confirm.show(ctx); );
} }
if self.refresh_confirm.visible { if self.search_menu.visible {
self.refresh_confirm.show(ctx); self.search_menu
} .show(ctx, &mut self.tabs, &mut self.selected_tab);
if self.exit_confirm.visible { }
self.exit_confirm.show(ctx); if self.close_tab_confirm.visible {
} self.close_tab_confirm.show(ctx);
if self.exit_confirm.proceed { }
for tab in self.tabs.iter_mut() { if self.refresh_confirm.visible {
tab.saved = true; self.refresh_confirm.show(ctx);
} }
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close); if self.exit_confirm.visible {
} self.exit_confirm.show(ctx);
if self.shortcuts_menu.visible { }
self.shortcuts_menu.show(ctx); if self.exit_confirm.proceed {
} for tab in self.tabs.iter_mut() {
if self.settings_menu.visible { tab.saved = true;
self.settings_menu.show(ctx); }
} egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close);
if self.settings_menu.updated { }
self.theme = self.settings_menu.theme; if self.shortcuts_menu.visible {
} self.shortcuts_menu.show(ctx);
}
if self.settings_menu.visible {
self.settings_menu.show(ctx);
}
if self.settings_menu.updated {
self.theme = self.settings_menu.theme;
}
self.handle_confirm(); self.handle_confirm();
} }
} }
fn to_syntax(language: &str) -> Syntax { fn to_syntax(language: &str) -> Syntax {
match language { match language {
"py" => Syntax::python(), "py" => Syntax::python(),
"rs" => Syntax::rust(), "rs" => Syntax::rust(),
_ => Syntax::shell(), _ => Syntax::shell(),
} }
} }
pub fn format_path(path: &Path) -> String { pub fn format_path(path: &Path) -> String {
let components: Vec<&OsStr> = path let components: Vec<&OsStr> = path
.components() .components()
.rev() .rev()
.take(DISPLAY_PATH_DEPTH) .take(DISPLAY_PATH_DEPTH)
.filter_map(|component| match component { .filter_map(|component| match component {
Component::RootDir | Component::CurDir => None, Component::RootDir | Component::CurDir => None,
_ => Some(component.as_os_str()), _ => Some(component.as_os_str()),
}) })
.collect(); .collect();
format!( format!(
"{}>", "{}>",
components components
.iter() .iter()
.rev() .rev()
.map(|&c| c.to_string_lossy()) .map(|&c| c.to_string_lossy())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("/") .join("/")
) )
} }

View file

@ -1,8 +1,7 @@
use eframe::egui; use eframe::egui;
use egui::{ use egui::{
FontFamily, FontFamily, FontId,
FontId, TextStyle::{Body, Button, Heading, Monospace, Small},
TextStyle::{Body, Button, Heading, Monospace, Small},
}; };
use homedir::get_my_home; use homedir::get_my_home;
use std::{ops::Range, path::PathBuf, sync::Arc, thread, time}; use std::{ops::Range, path::PathBuf, sync::Arc, thread, time};
@ -24,7 +23,7 @@ const TERMINAL_HEIGHT: f32 = 200.0;
const TERMINAL_RANGE: Range<f32> = 100.0..600.0; const TERMINAL_RANGE: Range<f32> = 100.0..600.0;
const RED: egui::Color32 = egui::Color32::from_rgb(235, 108, 99); const RED: egui::Color32 = egui::Color32::from_rgb(235, 108, 99);
const TIME_LABELS: [&str; 7] = [ const TIME_LABELS: [&str; 7] = [
"input", "settings", "tree", "terminal", "tabs", "content", "windows", "input", "settings", "tree", "terminal", "tabs", "content", "windows",
]; ];
const MAX_FPS: f32 = 30.0; const MAX_FPS: f32 = 30.0;
const DISPLAY_PATH_DEPTH: usize = 3; const DISPLAY_PATH_DEPTH: usize = 3;
@ -32,248 +31,263 @@ const MAX_TABS: usize = 20;
const MAX_PROJECT_COLUMNS: usize = 8; const MAX_PROJECT_COLUMNS: usize = 8;
fn main() -> Result<(), eframe::Error> { fn main() -> Result<(), eframe::Error> {
let icon_data = core::load_icon().unwrap_or_default(); let icon_data = core::load_icon().unwrap_or_default();
let options = eframe::NativeOptions { let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default() viewport: egui::ViewportBuilder::default()
.with_inner_size([1200.0, 800.0]) .with_inner_size([1200.0, 800.0])
.with_icon(Arc::new(icon_data)), .with_icon(Arc::new(icon_data)),
..Default::default() ..Default::default()
}; };
// Attempt to load previous state // Attempt to load previous state
let app_state: core::AppState = if save_path().exists() { let app_state: core::AppState = if save_path().exists() {
match core::load_state(save_path().as_path()) { match core::load_state(save_path().as_path()) {
Ok(app_state) => app_state, Ok(app_state) => app_state,
Err(_) => core::AppState::default(), Err(_) => core::AppState::default(),
} }
} else { } else {
core::AppState::default() core::AppState::default()
}; };
eframe::run_native( eframe::run_native(
&format!("Calcifer{}", TITLE), &format!("Calcifer{}", TITLE),
options, options,
Box::new(move |_cc| Box::from(Calcifer::from_app_state(app_state))), Box::new(move |_cc| Box::from(Calcifer::from_app_state(app_state))),
) )
} }
struct Calcifer { struct Calcifer {
selected_tab: panels::TabNumber, selected_tab: panels::TabNumber,
tabs: Vec<panels::Tab>, tabs: Vec<panels::Tab>,
command: String, command: String,
command_history: Vec<panels::CommandEntry>, command_history: Vec<panels::CommandEntry>,
theme: editor::ColorTheme, theme: editor::ColorTheme,
font_size: f32, font_size: f32,
project_mode: bool, project_mode: bool,
project_content: panels::Project, project_content: panels::Project,
home: PathBuf, home: PathBuf,
tree_dir_opened: Vec<String>, tree_dir_opened: Vec<String>,
file_tree: Option<panels::FileEntry>, file_tree: Option<panels::FileEntry>,
n_file_displayed: usize, n_file_displayed: usize,
tree_visible: bool, tree_visible: bool,
profiler_visible: bool, profiler_visible: bool,
terminal_visible: bool, terminal_visible: bool,
close_tab_confirm: sub_windows::ConfirmWindow, close_tab_confirm: sub_windows::ConfirmWindow,
tab_to_close: usize, tab_to_close: usize,
refresh_confirm: sub_windows::ConfirmWindow, refresh_confirm: sub_windows::ConfirmWindow,
exit_confirm: sub_windows::ConfirmWindow, exit_confirm: sub_windows::ConfirmWindow,
search_menu: sub_windows::SearchWindow, search_menu: sub_windows::SearchWindow,
settings_menu: sub_windows::SettingsWindow, settings_menu: sub_windows::SettingsWindow,
shortcuts_menu: sub_windows::ShortcutsWindow, shortcuts_menu: sub_windows::ShortcutsWindow,
time_watch: Vec<f32>, time_watch: Vec<f32>,
next_frame: time::Instant, next_frame: time::Instant,
} }
impl Default for Calcifer { impl Default for Calcifer {
fn default() -> Self { fn default() -> Self {
Self { Self {
selected_tab: panels::TabNumber::from_index(0), selected_tab: panels::TabNumber::from_index(0),
tabs: vec![panels::Tab::default()], tabs: vec![panels::Tab::default()],
command: String::new(), command: String::new(),
command_history: Vec::new(), command_history: Vec::new(),
theme: editor::themes::DEFAULT_THEMES[0], theme: editor::themes::DEFAULT_THEMES[0],
font_size: 14.0, font_size: 14.0,
project_mode: true, project_mode: true,
project_content: panels::Project::new(), project_content: panels::Project::new(),
home: get_my_home().unwrap().unwrap(), home: get_my_home().unwrap().unwrap(),
tree_dir_opened: vec![], tree_dir_opened: vec![],
file_tree: None, file_tree: None,
n_file_displayed: 0, n_file_displayed: 0,
tree_visible: false, tree_visible: false,
profiler_visible: false, profiler_visible: false,
terminal_visible: false, terminal_visible: false,
close_tab_confirm: sub_windows::ConfirmWindow::new( close_tab_confirm: sub_windows::ConfirmWindow::new(
"You have some unsaved changes, Do you still want to close this document ?", "You have some unsaved changes, Do you still want to close this document ?",
"Confirm Close", "Confirm Close",
), ),
tab_to_close: 0, tab_to_close: 0,
refresh_confirm: sub_windows::ConfirmWindow::new( refresh_confirm: sub_windows::ConfirmWindow::new(
"You have some unsaved changes, Do you still want to refresh this document ?", "You have some unsaved changes, Do you still want to refresh this document ?",
"Confirm Refresh", "Confirm Refresh",
), ),
exit_confirm: sub_windows::ConfirmWindow::new("", "Confirm Exit"), exit_confirm: sub_windows::ConfirmWindow::new("", "Confirm Exit"),
search_menu: sub_windows::SearchWindow::default(), search_menu: sub_windows::SearchWindow::default(),
settings_menu: sub_windows::SettingsWindow::new(editor::themes::DEFAULT_THEMES[0]), settings_menu: sub_windows::SettingsWindow::new(editor::themes::DEFAULT_THEMES[0]),
shortcuts_menu: sub_windows::ShortcutsWindow::new(), shortcuts_menu: sub_windows::ShortcutsWindow::new(),
time_watch: vec![0.0; TIME_LABELS.len()], time_watch: vec![0.0; TIME_LABELS.len()],
next_frame: time::Instant::now(), next_frame: time::Instant::now(),
} }
} }
} }
impl eframe::App for Calcifer { impl eframe::App for Calcifer {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
thread::sleep(time::Duration::from_secs_f32( thread::sleep(time::Duration::from_secs_f32(
((1.0 / MAX_FPS) - self.next_frame.elapsed().as_secs_f32()).max(0.0), ((1.0 / MAX_FPS) - self.next_frame.elapsed().as_secs_f32()).max(0.0),
)); ));
self.next_frame = time::Instant::now(); self.next_frame = time::Instant::now();
let mut watch = time::Instant::now(); let mut watch = time::Instant::now();
let mut style = (*ctx.style()).clone(); let mut style = (*ctx.style()).clone();
style.text_styles = [ style.text_styles = [
(Heading, FontId::new(self.font_size * 1.6, FontFamily::Proportional)), (
(Body, FontId::new(self.font_size, FontFamily::Proportional)), Heading,
(Monospace, FontId::new(self.font_size, FontFamily::Monospace)), FontId::new(self.font_size * 1.6, FontFamily::Proportional),
(Button, FontId::new(self.font_size, FontFamily::Proportional)), ),
(Small, FontId::new(self.font_size, FontFamily::Proportional)), (Body, FontId::new(self.font_size, FontFamily::Proportional)),
] (
.into(); Monospace,
ctx.set_style(style); FontId::new(self.font_size, FontFamily::Monospace),
),
(
Button,
FontId::new(self.font_size, FontFamily::Proportional),
),
(Small, FontId::new(self.font_size, FontFamily::Proportional)),
]
.into();
ctx.set_style(style);
if ctx.input(|i| i.key_pressed(egui::Key::R) && i.modifiers.ctrl) if ctx.input(|i| i.key_pressed(egui::Key::R) && i.modifiers.ctrl)
&& !self.refresh_confirm.visible && !self.refresh_confirm.visible
{ {
if self.tabs[self.selected_tab.to_index()].saved { if self.tabs[self.selected_tab.to_index()].saved {
self.tabs[self.selected_tab.to_index()].refresh(); self.tabs[self.selected_tab.to_index()].refresh();
} else { } else {
self.refresh_confirm.ask(); self.refresh_confirm.ask();
} }
} }
if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) { if ctx.input(|i| i.key_pressed(egui::Key::Enter))
self.handle_save_file(self.save_tab()); && self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION
} {
self.project_content.item_window.visible = true;
}
if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl && i.modifiers.shift) { if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
self.handle_save_file(self.save_tab_as()); self.handle_save_file(self.save_tab());
} }
if ctx.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.alt) { if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl && i.modifiers.shift) {
self.move_through_tabs(false); self.handle_save_file(self.save_tab_as());
} }
if ctx.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.alt) { if ctx.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.alt) {
self.move_through_tabs(true); self.move_through_tabs(false);
} }
if ctx.input(|i| i.zoom_delta() > 1.0) { if ctx.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.alt) {
self.font_size = (self.font_size * 1.1).min(30.0); self.move_through_tabs(true);
} }
if ctx.input(|i| i.zoom_delta() < 1.0) { if ctx.input(|i| i.zoom_delta() > 1.0) {
self.font_size = (self.font_size / 1.1).max(10.0); self.font_size = (self.font_size * 1.1).min(30.0);
} }
if ctx.input(|i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) { if ctx.input(|i| i.zoom_delta() < 1.0) {
self.search_menu.visible = !self.search_menu.visible; self.font_size = (self.font_size / 1.1).max(10.0);
self.search_menu.initialized = !self.search_menu.visible; }
}
if ctx.input(|i| i.viewport().close_requested()) { if ctx.input(|i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) {
let mut unsaved_tabs: Vec<usize> = vec![]; self.search_menu.visible = !self.search_menu.visible;
for (index, tab) in self.tabs.iter().enumerate() { self.search_menu.initialized = !self.search_menu.visible;
if !tab.saved { }
unsaved_tabs.push(index);
}
}
if !unsaved_tabs.is_empty() {
let mut unsaved_tabs_names: String = "".to_string();
for index in unsaved_tabs.iter() {
unsaved_tabs_names.push_str(&self.tabs[*index].get_name());
}
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::CancelClose);
self.exit_confirm.prompt = format!(
"You have some unsaved changes :\n{}\nDo you still want to exit ?",
unsaved_tabs_names
);
self.exit_confirm.ask();
}
}
self.time_watch[0] = watch.elapsed().as_micros() as f32 / 1000.0; if ctx.input(|i| i.viewport().close_requested()) {
watch = time::Instant::now(); let mut unsaved_tabs: Vec<usize> = vec![];
for (index, tab) in self.tabs.iter().enumerate() {
if !tab.saved {
unsaved_tabs.push(index);
}
}
if !unsaved_tabs.is_empty() {
let mut unsaved_tabs_names: String = "".to_string();
for index in unsaved_tabs.iter() {
unsaved_tabs_names.push_str(&self.tabs[*index].get_name());
}
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::CancelClose);
self.exit_confirm.prompt = format!(
"You have some unsaved changes :\n{}\nDo you still want to exit ?",
unsaved_tabs_names
);
self.exit_confirm.ask();
}
}
self.draw_settings(ctx); self.time_watch[0] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now();
self.time_watch[1] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_settings(ctx);
watch = time::Instant::now();
self.draw_tree_panel(ctx); self.time_watch[1] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now();
self.time_watch[2] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_tree_panel(ctx);
watch = time::Instant::now();
self.draw_bottom_tray(ctx); self.time_watch[2] = watch.elapsed().as_micros() as f32 / 1000.0;
self.draw_terminal_panel(ctx); watch = time::Instant::now();
self.time_watch[3] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_bottom_tray(ctx);
watch = time::Instant::now(); self.draw_terminal_panel(ctx);
self.draw_tab_panel(ctx); self.time_watch[3] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now();
self.time_watch[4] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_tab_panel(ctx);
watch = time::Instant::now();
self.draw_content_panel(ctx); self.time_watch[4] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now();
self.time_watch[5] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_content_panel(ctx);
watch = time::Instant::now();
self.draw_windows(ctx); self.time_watch[5] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now();
self.time_watch[6] = watch.elapsed().as_micros() as f32 / 1000.0; self.draw_windows(ctx);
}
fn on_exit(&mut self, _gl: std::option::Option<&eframe::glow::Context>) { self.time_watch[6] = watch.elapsed().as_micros() as f32 / 1000.0;
self.save_state(); }
}
fn on_exit(&mut self, _gl: std::option::Option<&eframe::glow::Context>) {
self.save_state();
}
} }
fn save_path() -> PathBuf { fn save_path() -> PathBuf {
if TITLE.is_empty() { if TITLE.is_empty() {
get_my_home() get_my_home()
.unwrap() .unwrap()
.unwrap() .unwrap()
.as_path() .as_path()
.join(".calcifer") .join(".calcifer")
.join("save.json") .join("save.json")
.to_path_buf() .to_path_buf()
} else { } else {
get_my_home() get_my_home()
.unwrap() .unwrap()
.unwrap() .unwrap()
.as_path() .as_path()
.join(".calcifer") .join(".calcifer")
.join("debug") .join("debug")
.join("save.json") .join("save.json")
.to_path_buf() .to_path_buf()
} }
} }

View file

@ -1,131 +1,223 @@
use eframe::egui; use eframe::egui;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::{
cmp::min,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::MAX_PROJECT_COLUMNS;
use crate::core::hex_str_to_color; use crate::core::hex_str_to_color;
use crate::editor::ColorTheme; use crate::editor::ColorTheme;
use crate::sub_windows;
use crate::MAX_PROJECT_COLUMNS;
#[derive(Clone)]
pub struct Project { pub struct Project {
categories: Vec<Category>, pub categories: Vec<Category>,
selected_item: Option<[usize; 2]>, pub selected_item: Location,
pub item_window: sub_windows::ProjectItemWindow,
was_moving: bool,
} }
impl Project { impl Project {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
categories: vec![Category::create()], categories: vec![Category::create()],
selected_item: None, selected_item: Location::zero(),
} was_moving: false,
} item_window: sub_windows::ProjectItemWindow::new(),
}
fn add_category(&mut self) { }
let last = self.categories.len() - 1;
self.categories[last].initialize(); fn add_category(&mut self) {
if self.categories.len() < MAX_PROJECT_COLUMNS { let last = self.categories.len() - 1;
self.categories.push(Category::create()); self.categories[last].initialize();
} if self.categories.len() < MAX_PROJECT_COLUMNS {
} self.categories.push(Category::create());
}
fn delete_category(&mut self, index: usize) { }
self.categories.remove(index);
let last = self.categories.len() - 1; fn delete_category(&mut self, index: usize) {
if self.categories[last].name != "+" { self.categories.remove(index);
self.categories.push(Category::create()); let last = self.categories.len() - 1;
} if self.categories[last].name != "+" {
} self.categories.push(Category::create());
}
}
} }
#[derive(Clone)] #[derive(Clone)]
struct Category { pub struct Category {
name: String, name: String,
content: Vec<Item>, pub content: Vec<Item>,
} }
impl Category { impl Category {
fn create() -> Self { fn create() -> Self {
Self { Self {
name: "+".into(), name: "+".into(),
content: vec![], content: vec![],
} }
} }
fn initialize(&mut self) { fn initialize(&mut self) {
self.name = "untitled".into(); self.name = "untitled".into();
} }
} }
#[derive(Clone, Hash)] #[derive(Clone, Hash)]
struct Item { pub struct Item {
name: String, pub name: String,
description: String, pub description: String,
id: usize, id: usize,
} }
impl Item { impl Item {
fn new(name: &str) -> Self { fn new(name: &str) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
description: "".to_string(), description: "".to_string(),
id: get_id(), id: get_id(),
} }
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Location { pub struct Location {
category: usize, pub category: usize,
row: usize, pub row: usize,
} }
impl Location {
fn zero() -> Self {
Self {
category: 0,
row: 0,
}
}
}
fn get_id() -> usize { fn get_id() -> usize {
static COUNTER:AtomicUsize = AtomicUsize::new(1); static COUNTER: AtomicUsize = AtomicUsize::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed) COUNTER.fetch_add(1, Ordering::Relaxed)
} }
pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project) { pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project) {
ui.columns(MAX_PROJECT_COLUMNS, |uis| { ui.columns(MAX_PROJECT_COLUMNS, |uis| {
for (category_index, category) in project.categories.clone().into_iter().enumerate() { for (category_index, category) in project.categories.clone().into_iter().enumerate() {
let ui = &mut uis[category_index]; let ui = &mut uis[category_index];
if category.name == "+" { if category.name == "+" {
if ui.add(egui::Button::new("+")).clicked() { if ui.add(egui::Button::new("+")).clicked() {
project.add_category(); project.add_category();
} }
} else { } else {
let response = ui.add(egui::TextEdit::singleline(&mut project.categories[category_index].name).desired_width(f32::INFINITY)); let response = ui.add(
if response.lost_focus() && project.categories[category_index].name.is_empty() { egui::TextEdit::singleline(&mut project.categories[category_index].name)
project.delete_category(category_index); .desired_width(f32::INFINITY),
} );
} if response.lost_focus() && project.categories[category_index].name.is_empty() {
project.delete_category(category_index);
for (item_index, item) in category.content.iter().enumerate() { }
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| { }
ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.literals));
if ui.add(egui::Button::new("")).clicked() { for (item_index, item) in category.content.iter().enumerate() {
println!("yes"); if project.selected_item
} == (Location {
category: category_index,
if project.selected_item == Some([category_index, item_index]) { row: item_index,
ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.bg)); })
ui.add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.functions))); {
} else { ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.bg));
ui.style_mut().visuals.override_text_color = Some(hex_str_to_color(theme.literals)); ui.add(
if ui.add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg))).clicked() { egui::Button::new(item.name.clone())
project.selected_item = Some([category_index, item_index]); .fill(hex_str_to_color(theme.functions)),
} );
} } else {
}); ui.style_mut().visuals.override_text_color =
} Some(hex_str_to_color(theme.literals));
if ui
if category.name != "+" { .add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg)))
if ui.add(egui::Button::new("+")).clicked() { .clicked()
project.categories[category_index].content.push(Item::new("item")); {
} project.selected_item = Location {
} category: category_index,
} row: item_index,
}); };
} }
}
}
if category.name != "+" {
if ui.add(egui::Button::new("+")).clicked() {
project.categories[category_index]
.content
.push(Item::new("item"));
}
}
}
});
let mut moved = false;
let category = project.selected_item.category.clone();
let row = project.selected_item.row.clone();
if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft)) && project.selected_item.category > 0 {
moved = true;
if !project.was_moving {
project.selected_item.category -= 1;
project.selected_item.row = min(
project.categories[category].content.len() - 1,
project.selected_item.row,
);
}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight))
&& project.selected_item.category < project.categories.len() - 2
{
moved = true;
if !project.was_moving {
project.selected_item.category += 1;
project.selected_item.row = min(
project.categories[category].content.len() - 1,
project.selected_item.row,
);
}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) && project.selected_item.row > 0 {
moved = true;
if !project.was_moving {
project.selected_item.row -= 1;
}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown))
&& project.selected_item.row < project.categories[category].content.len() - 1
{
moved = true;
if !project.was_moving {
project.selected_item.row += 1;
}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.shift) {
moved = true;
if !project.was_moving {}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.shift) {
moved = true;
if !project.was_moving {}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp) && i.modifiers.shift)
&& project.selected_item.row > 0
{
moved = true;
if !project.was_moving {
let temp = project.categories[category].content[row].clone();
project.categories[category].content[row] =
project.categories[category].content[row - 1].clone();
project.categories[category].content[row - 1] = temp.clone();
project.selected_item.row -= 1;
}
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown) && i.modifiers.shift) {
moved = true;
if !project.was_moving {
let temp = project.categories[category].content[row].clone();
project.categories[category].content[row] =
project.categories[category].content[row + 1].clone();
project.categories[category].content[row + 1] = temp.clone();
project.selected_item.row += 1;
}
}
project.was_moving = moved;
}

View file

@ -3,191 +3,191 @@ use crate::core::format_path;
use arboard::Clipboard; use arboard::Clipboard;
use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::fcntl::{fcntl, FcntlArg, OFlag};
use std::{ use std::{
env, env,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
os::fd::AsRawFd, os::fd::AsRawFd,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
}; };
pub struct Buffer { pub struct Buffer {
pub output_buffer: BufReader<std::process::ChildStdout>, pub output_buffer: BufReader<std::process::ChildStdout>,
pub error_buffer: BufReader<std::process::ChildStderr>, pub error_buffer: BufReader<std::process::ChildStderr>,
pub child: Child, pub child: Child,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Line { pub struct Line {
pub text: String, pub text: String,
pub error: bool, pub error: bool,
} }
impl Line { impl Line {
fn output(text: String) -> Self { fn output(text: String) -> Self {
Self { Self {
text: remove_line_break(text), text: remove_line_break(text),
error: false, error: false,
} }
} }
fn error(text: String) -> Self { fn error(text: String) -> Self {
Self { Self {
text: remove_line_break(text), text: remove_line_break(text),
error: true, error: true,
} }
} }
} }
pub struct CommandEntry { pub struct CommandEntry {
pub env: String, pub env: String,
pub command: String, pub command: String,
pub result: Vec<Line>, pub result: Vec<Line>,
pub buffer: Option<Buffer>, pub buffer: Option<Buffer>,
pub finished: bool, pub finished: bool,
} }
impl CommandEntry { impl CommandEntry {
pub fn new(env: String, command: String) -> Self { pub fn new(env: String, command: String) -> Self {
let (buffer, result) = match execute(command.clone()) { let (buffer, result) = match execute(command.clone()) {
Ok(command_buffer) => (Some(command_buffer), vec![]), Ok(command_buffer) => (Some(command_buffer), vec![]),
Err(err) => ( Err(err) => (
None, None,
vec![Line::error(format!("failed to get results: {}", err))], vec![Line::error(format!("failed to get results: {}", err))],
), ),
}; };
CommandEntry { CommandEntry {
env, env,
command, command,
result, result,
buffer, buffer,
finished: false, finished: false,
} }
} }
pub fn update(&mut self) { pub fn update(&mut self) {
if let Some(buffer) = &mut self.buffer { if let Some(buffer) = &mut self.buffer {
let mut output = String::new(); let mut output = String::new();
loop { loop {
let _ = buffer.output_buffer.read_line(&mut output); let _ = buffer.output_buffer.read_line(&mut output);
if !output.to_string().is_empty() { if !output.to_string().is_empty() {
self.result.push(Line::output(format!("{}\n", output))); self.result.push(Line::output(format!("{}\n", output)));
output = "".to_string() output = "".to_string()
} else { } else {
break; break;
} }
} }
let mut error = String::new(); let mut error = String::new();
loop { loop {
let _ = buffer.error_buffer.read_line(&mut error); let _ = buffer.error_buffer.read_line(&mut error);
if !error.to_string().is_empty() { if !error.to_string().is_empty() {
self.result.push(Line::error(format!("{}\n", error))); self.result.push(Line::error(format!("{}\n", error)));
error = "".to_string() error = "".to_string()
} else { } else {
break; break;
} }
} }
if let Ok(Some(_exit_status)) = buffer.child.try_wait() { if let Ok(Some(_exit_status)) = buffer.child.try_wait() {
//self.result.push(Line::output(format!("Command finished with status: {:?}\n", exit_status))); //self.result.push(Line::output(format!("Command finished with status: {:?}\n", exit_status)));
self.finished = true; self.finished = true;
} }
} }
} }
pub fn copy_error_code(&self) { pub fn copy_error_code(&self) {
let mut txt: String = "".to_string(); let mut txt: String = "".to_string();
for line in self.result.iter() { for line in self.result.iter() {
if line.error { if line.error {
txt.push_str(&format!("{}\n", line.text)); txt.push_str(&format!("{}\n", line.text));
} }
} }
let mut _clipboard = Clipboard::new().expect("Failed to initialize clipboard"); let mut _clipboard = Clipboard::new().expect("Failed to initialize clipboard");
_clipboard.set_text(txt).unwrap(); _clipboard.set_text(txt).unwrap();
} }
} }
pub fn send_command(command: String) -> CommandEntry { pub fn send_command(command: String) -> CommandEntry {
let env = format_path(&env::current_dir().unwrap_or_else(|_| PathBuf::from("/"))); let env = format_path(&env::current_dir().unwrap_or_else(|_| PathBuf::from("/")));
if command.len() < 2 { if command.len() < 2 {
return CommandEntry::new(env, command); return CommandEntry::new(env, command);
} }
if &command[..2] != "cd" { if &command[..2] != "cd" {
return CommandEntry::new(env, command); return CommandEntry::new(env, command);
} }
if command.len() < 4 { if command.len() < 4 {
let mut entry = let mut entry =
CommandEntry::new(env, "echo Invalid cd, should provide path >&2".to_string()); CommandEntry::new(env, "echo Invalid cd, should provide path >&2".to_string());
entry.command = command; entry.command = command;
return entry; return entry;
} }
let path_append = command[3..].replace('~', "/home/penwing"); let path_append = command[3..].replace('~', "/home/penwing");
let path = Path::new(&path_append); let path = Path::new(&path_append);
if format!("{}", path.display()) == "/" { if format!("{}", path.display()) == "/" {
let mut entry = CommandEntry::new(env, "echo Root access denied >&2".to_string()); let mut entry = CommandEntry::new(env, "echo Root access denied >&2".to_string());
entry.command = command; entry.command = command;
return entry; return entry;
} }
if env::set_current_dir(path).is_ok() { if env::set_current_dir(path).is_ok() {
let mut entry = CommandEntry::new(env, format!("echo Moved to : {}", path.display())); let mut entry = CommandEntry::new(env, format!("echo Moved to : {}", path.display()));
entry.command = command; entry.command = command;
entry entry
} else { } else {
let mut entry = CommandEntry::new( let mut entry = CommandEntry::new(
env, env,
format!("echo Could not find path : {} >&2", path.display()), format!("echo Could not find path : {} >&2", path.display()),
); );
entry.command = command; entry.command = command;
entry entry
} }
} }
pub fn execute(command: String) -> Result<Buffer, std::io::Error> { pub fn execute(command: String) -> Result<Buffer, std::io::Error> {
let mut child = Command::new("sh") let mut child = Command::new("sh")
.arg("-c") .arg("-c")
.arg(command.clone()) .arg(command.clone())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn()?; .spawn()?;
let stdout = child let stdout = child
.stdout .stdout
.take() .take()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Failed to open stdout"))?; .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Failed to open stdout"))?;
let stderr = child let stderr = child
.stderr .stderr
.take() .take()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Failed to open stderr"))?; .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Failed to open stderr"))?;
let stdout_fd = stdout.as_raw_fd(); let stdout_fd = stdout.as_raw_fd();
let stderr_fd = stderr.as_raw_fd(); let stderr_fd = stderr.as_raw_fd();
fcntl(stdout_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))?; fcntl(stdout_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))?;
fcntl(stderr_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))?; fcntl(stderr_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))?;
let output_buffer = BufReader::new(stdout); let output_buffer = BufReader::new(stdout);
let error_buffer = BufReader::new(stderr); let error_buffer = BufReader::new(stderr);
Ok(Buffer { Ok(Buffer {
output_buffer, output_buffer,
error_buffer, error_buffer,
child, child,
}) })
} }
fn remove_line_break(input: String) -> String { fn remove_line_break(input: String) -> String {
let mut text = input.clone(); let mut text = input.clone();
while text.ends_with('\n') { while text.ends_with('\n') {
text.pop(); text.pop();
if text.ends_with('\r') { if text.ends_with('\r') {
text.pop(); text.pop();
} }
} }
text text
} }

View file

@ -10,3 +10,6 @@ pub use settings::*;
mod shortcuts; mod shortcuts;
pub use shortcuts::*; pub use shortcuts::*;
mod project_item;
pub use project_item::*;

View file

@ -0,0 +1,28 @@
use eframe::egui;
use crate::panels;
pub struct ProjectItemWindow {
pub visible: bool,
}
impl ProjectItemWindow {
pub fn new() -> Self {
Self { visible: false }
}
pub fn show(&mut self, ctx: &egui::Context, item: &mut panels::Item) {
let mut visible = self.visible;
egui::Window::new("Project Item")
.open(&mut visible)
.vscroll(true)
.hscroll(true)
.show(ctx, |ui| self.ui(ui, item));
self.visible = self.visible && visible;
}
fn ui(&mut self, ui: &mut egui::Ui, item: &mut panels::Item) {
ui.set_min_width(250.0);
ui.label(item.name.clone());
}
}

View file

@ -235,9 +235,7 @@ impl SearchWindow {
} }
fn replace(&mut self, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) { fn replace(&mut self, tabs: &mut Vec<Tab>, selected_tab: &mut TabNumber) {
if self.searched_text != self.search_text { self.search(tabs, &mut *selected_tab);
self.search(tabs, &mut *selected_tab);
}
let mut done: Vec<TabNumber> = vec![]; let mut done: Vec<TabNumber> = vec![];
for element in &self.results { for element in &self.results {