finished item edit window, and lateral item movement

This commit is contained in:
Penwing 2024-02-02 18:55:39 +01:00
parent afec8f6426
commit 95bfd219e9
5 changed files with 768 additions and 735 deletions

View file

@ -15,345 +15,354 @@ 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_content.item_window.visible = self.toggle(ui, self.project_content.item_window.visible, "🖊");
// } }
}); });
}); });
} }
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);
let mut running_command = false;
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(); running_command = true;
let _ = ui.link("(⌛)"); entry.update();
} else if ui.link("(🗐)").clicked() { let _ = ui.link("(⌛)");
entry.copy_error_code(); } else if ui.link("(🗐)").clicked() {
} entry.copy_error_code();
ui.colored_label( }
command_color, ui.colored_label(
format!("{} {}", entry.env, entry.command), command_color,
); 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( ui.label(
egui::RichText::new(&line.text) egui::RichText::new(&line.text)
.monospace() .monospace()
.color(color), .color(color),
); );
} }
} }
});
}); self.running_command = running_command;
}); });
}); });
} });
});
}
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.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.project_content.item_window.visible { if self.project_content.item_window.visible {
self.project_content.item_window.show( if self.project_content.categories.len() > 1 && self.project_content.categories[self.project_content.selected_item.category].content.len() > 0 {
ctx, self.project_content.item_window.show(
&mut self.project_content.categories[self.project_content.selected_item.category] ctx,
.content[self.project_content.selected_item.row], &mut self.project_content.categories[self.project_content.selected_item.category]
); .content[self.project_content.selected_item.row],
} );
if self.search_menu.visible { } else {
self.search_menu
.show(ctx, &mut self.tabs, &mut self.selected_tab); self.project_content.item_window.visible = false;
} }
if self.close_tab_confirm.visible { }
self.close_tab_confirm.show(ctx); if self.search_menu.visible {
} self.search_menu
if self.refresh_confirm.visible { .show(ctx, &mut self.tabs, &mut self.selected_tab);
self.refresh_confirm.show(ctx); }
} if self.close_tab_confirm.visible {
if self.exit_confirm.visible { self.close_tab_confirm.show(ctx);
self.exit_confirm.show(ctx); }
} if self.refresh_confirm.visible {
if self.exit_confirm.proceed { self.refresh_confirm.show(ctx);
for tab in self.tabs.iter_mut() { }
tab.saved = true; if self.exit_confirm.visible {
} self.exit_confirm.show(ctx);
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close); }
} if self.exit_confirm.proceed {
if self.shortcuts_menu.visible { for tab in self.tabs.iter_mut() {
self.shortcuts_menu.show(ctx); tab.saved = true;
} }
if self.settings_menu.visible { egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close);
self.settings_menu.show(ctx); }
} if self.shortcuts_menu.visible {
if self.settings_menu.updated { self.shortcuts_menu.show(ctx);
self.theme = self.settings_menu.theme; }
} 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,7 +1,7 @@
use eframe::egui; use eframe::egui;
use egui::{ use egui::{
FontFamily, FontId, FontFamily, 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};
@ -23,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;
@ -31,263 +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>,
running_command: bool,
theme: editor::ColorTheme, theme: editor::ColorTheme,
font_size: f32, font_size: f32,
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(),
running_command: false,
theme: editor::themes::DEFAULT_THEMES[0], theme: editor::themes::DEFAULT_THEMES[0],
font_size: 14.0, font_size: 14.0,
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, Heading,
FontId::new(self.font_size * 1.6, FontFamily::Proportional), FontId::new(self.font_size * 1.6, FontFamily::Proportional),
), ),
(Body, FontId::new(self.font_size, FontFamily::Proportional)), (Body, FontId::new(self.font_size, FontFamily::Proportional)),
( (
Monospace, Monospace,
FontId::new(self.font_size, FontFamily::Monospace), FontId::new(self.font_size, FontFamily::Monospace),
), ),
( (
Button, Button,
FontId::new(self.font_size, FontFamily::Proportional), FontId::new(self.font_size, FontFamily::Proportional),
), ),
(Small, FontId::new(self.font_size, FontFamily::Proportional)), (Small, FontId::new(self.font_size, FontFamily::Proportional)),
] ]
.into(); .into();
ctx.set_style(style); 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::Enter)) if ctx.input(|i| i.key_pressed(egui::Key::Enter))
&& self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION && self.tabs[self.selected_tab.to_index()].language == PROJECT_EXTENSION
{ {
self.project_content.item_window.visible = true; self.project_content.item_window.visible = true;
} }
if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) { if ctx.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
self.handle_save_file(self.save_tab()); self.handle_save_file(self.save_tab());
} }
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 && i.modifiers.shift) {
self.handle_save_file(self.save_tab_as()); self.handle_save_file(self.save_tab_as());
} }
if ctx.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.alt) { if ctx.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.alt) {
self.move_through_tabs(false); self.move_through_tabs(false);
} }
if ctx.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.alt) { if ctx.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.alt) {
self.move_through_tabs(true); 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).min(30.0); self.font_size = (self.font_size * 1.1).min(30.0);
} }
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).max(10.0);
} }
if ctx.input(|i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) { if ctx.input(|i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) {
self.search_menu.visible = !self.search_menu.visible; self.search_menu.visible = !self.search_menu.visible;
self.search_menu.initialized = !self.search_menu.visible; self.search_menu.initialized = !self.search_menu.visible;
} }
if ctx.input(|i| i.viewport().close_requested()) { if ctx.input(|i| i.viewport().close_requested()) {
let mut unsaved_tabs: Vec<usize> = vec![]; let mut unsaved_tabs: Vec<usize> = vec![];
for (index, tab) in self.tabs.iter().enumerate() { for (index, tab) in self.tabs.iter().enumerate() {
if !tab.saved { if !tab.saved {
unsaved_tabs.push(index); unsaved_tabs.push(index);
} }
} }
if !unsaved_tabs.is_empty() { if !unsaved_tabs.is_empty() {
let mut unsaved_tabs_names: String = "".to_string(); let mut unsaved_tabs_names: String = "".to_string();
for index in unsaved_tabs.iter() { for index in unsaved_tabs.iter() {
unsaved_tabs_names.push_str(&self.tabs[*index].get_name()); unsaved_tabs_names.push_str(&self.tabs[*index].get_name());
} }
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::CancelClose); egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::CancelClose);
self.exit_confirm.prompt = format!( self.exit_confirm.prompt = format!(
"You have some unsaved changes :\n{}\nDo you still want to exit ?", "You have some unsaved changes :\n{}\nDo you still want to exit ?",
unsaved_tabs_names unsaved_tabs_names
); );
self.exit_confirm.ask(); self.exit_confirm.ask();
} }
} }
self.time_watch[0] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[0] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_settings(ctx); self.draw_settings(ctx);
self.time_watch[1] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[1] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_tree_panel(ctx); self.draw_tree_panel(ctx);
self.time_watch[2] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[2] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_bottom_tray(ctx); self.draw_bottom_tray(ctx);
self.draw_terminal_panel(ctx); self.draw_terminal_panel(ctx);
self.time_watch[3] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[3] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_tab_panel(ctx); self.draw_tab_panel(ctx);
self.time_watch[4] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[4] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_content_panel(ctx); self.draw_content_panel(ctx);
self.time_watch[5] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[5] = watch.elapsed().as_micros() as f32 / 1000.0;
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_windows(ctx); self.draw_windows(ctx);
self.time_watch[6] = watch.elapsed().as_micros() as f32 / 1000.0; self.time_watch[6] = watch.elapsed().as_micros() as f32 / 1000.0;
} }
fn on_exit(&mut self, _gl: std::option::Option<&eframe::glow::Context>) { fn on_exit(&mut self, _gl: std::option::Option<&eframe::glow::Context>) {
self.save_state(); 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,7 +1,7 @@
use eframe::egui; use eframe::egui;
use std::{ use std::{
cmp::min, cmp::min,
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
use crate::core::hex_str_to_color; use crate::core::hex_str_to_color;
@ -10,214 +10,229 @@ use crate::sub_windows;
use crate::MAX_PROJECT_COLUMNS; use crate::MAX_PROJECT_COLUMNS;
pub struct Project { pub struct Project {
pub categories: Vec<Category>, pub categories: Vec<Category>,
pub selected_item: Location, pub selected_item: Location,
pub item_window: sub_windows::ProjectItemWindow, pub item_window: sub_windows::ProjectItemWindow,
was_moving: bool, 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: Location::zero(), selected_item: Location::zero(),
was_moving: false, was_moving: false,
item_window: sub_windows::ProjectItemWindow::new(), item_window: sub_windows::ProjectItemWindow::new(),
} }
} }
fn add_category(&mut self) { fn add_category(&mut self) {
let last = self.categories.len() - 1; let last = self.categories.len() - 1;
self.categories[last].initialize(); self.categories[last].initialize();
if self.categories.len() < MAX_PROJECT_COLUMNS { if self.categories.len() < MAX_PROJECT_COLUMNS {
self.categories.push(Category::create()); self.categories.push(Category::create());
} }
} }
fn delete_category(&mut self, index: usize) { fn delete_category(&mut self, index: usize) {
self.categories.remove(index); self.categories.remove(index);
let last = self.categories.len() - 1; let last = self.categories.len() - 1;
if self.categories[last].name != "+" { if self.categories[last].name != "+" {
self.categories.push(Category::create()); self.categories.push(Category::create());
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Category { pub struct Category {
name: String, name: String,
pub 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)]
pub struct Item { pub struct Item {
pub name: String, pub name: String,
pub 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: "// Hello there".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 {
pub category: usize, pub category: usize,
pub row: usize, pub row: usize,
} }
impl Location { impl Location {
fn zero() -> Self { fn zero() -> Self {
Self { Self {
category: 0, category: 0,
row: 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 { continue;
let response = ui.add( } else {
egui::TextEdit::singleline(&mut project.categories[category_index].name) let response = ui.add(
.desired_width(f32::INFINITY), egui::TextEdit::singleline(&mut project.categories[category_index].name)
); .desired_width(f32::INFINITY),
if response.lost_focus() && project.categories[category_index].name.is_empty() { );
project.delete_category(category_index); if response.lost_focus() && project.categories[category_index].name.is_empty() {
} project.delete_category(category_index);
} }
}
ui.separator();
for (item_index, item) in category.content.iter().enumerate() { for (item_index, item) in category.content.iter().enumerate() {
if project.selected_item if project.selected_item
== (Location { == (Location {
category: category_index, category: category_index,
row: item_index, row: item_index,
}) })
{ {
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.bg));
ui.add( ui.add(
egui::Button::new(item.name.clone()) egui::Button::new(item.name.clone())
.fill(hex_str_to_color(theme.functions)), .fill(hex_str_to_color(theme.functions)),
); );
} else { } else {
ui.style_mut().visuals.override_text_color = ui.style_mut().visuals.override_text_color =
Some(hex_str_to_color(theme.literals)); Some(hex_str_to_color(theme.literals));
if ui if ui
.add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg))) .add(egui::Button::new(item.name.clone()).fill(hex_str_to_color(theme.bg)))
.clicked() .clicked()
{ {
project.selected_item = Location { project.selected_item = Location {
category: category_index, category: category_index,
row: item_index, row: item_index,
}; };
} }
} }
} }
if category.name != "+" { if category.name != "+" {
if ui.add(egui::Button::new("+")).clicked() { if ui.add(egui::Button::new("+")).clicked() {
project.categories[category_index] project.categories[category_index]
.content .content
.push(Item::new("item")); .push(Item::new("item"));
} }
} }
} }
}); });
let mut moved = false; let mut moved = false;
let category = project.selected_item.category.clone(); let category = project.selected_item.category.clone();
let row = project.selected_item.row.clone(); let row = project.selected_item.row.clone();
if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft)) && project.selected_item.category > 0 { if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.shift) && project.selected_item.category > 0 {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.category -= 1; let temp = project.categories[category].content[row].clone();
project.selected_item.row = min( project.categories[category - 1].content.push(temp);
project.categories[category].content.len() - 1, project.categories[category].content.remove(row);
project.selected_item.row, project.selected_item.category -= 1;
); project.selected_item.row = project.categories[category - 1].content.len() - 1;
} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight)) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.shift) && project.selected_item.category < project.categories.len() - 2 {
&& project.selected_item.category < project.categories.len() - 2 moved = true;
{ if !project.was_moving {
moved = true; let temp = project.categories[category].content[row].clone();
if !project.was_moving { project.categories[category + 1].content.push(temp);
project.selected_item.category += 1; project.categories[category].content.remove(row);
project.selected_item.row = min( project.selected_item.category += 1;
project.categories[category].content.len() - 1, project.selected_item.row = project.categories[category + 1].content.len() - 1;
project.selected_item.row, }
); } else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp) && i.modifiers.shift)
} && project.selected_item.row > 0
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) && project.selected_item.row > 0 { {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
project.selected_item.row -= 1; let temp = project.categories[category].content[row].clone();
} project.categories[category].content[row] =
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) project.categories[category].content[row - 1].clone();
&& project.selected_item.row < project.categories[category].content.len() - 1 project.categories[category].content[row - 1] = temp.clone();
{ project.selected_item.row -= 1;
moved = true; }
if !project.was_moving { } else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown) && i.modifiers.shift) {
project.selected_item.row += 1; moved = true;
} if !project.was_moving {
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft) && i.modifiers.shift) { let temp = project.categories[category].content[row].clone();
moved = true; project.categories[category].content[row] =
if !project.was_moving {} project.categories[category].content[row + 1].clone();
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight) && i.modifiers.shift) { project.categories[category].content[row + 1] = temp.clone();
moved = true; project.selected_item.row += 1;
if !project.was_moving {} }
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp) && i.modifiers.shift) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowLeft)) && project.selected_item.category > 0 {
&& project.selected_item.row > 0 moved = true;
{ if !project.was_moving {
moved = true; project.selected_item.category -= 1;
if !project.was_moving { project.selected_item.row = min(
let temp = project.categories[category].content[row].clone(); project.categories[category].content.len() - 1,
project.categories[category].content[row] = project.selected_item.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::ArrowRight))
} && project.selected_item.category < project.categories.len() - 2
} else if ui.input(|i| i.key_pressed(egui::Key::ArrowDown) && i.modifiers.shift) { {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving {
let temp = project.categories[category].content[row].clone(); project.selected_item.category += 1;
project.categories[category].content[row] = project.selected_item.row = min(
project.categories[category].content[row + 1].clone(); project.categories[category].content.len() - 1,
project.categories[category].content[row + 1] = temp.clone(); project.selected_item.row,
project.selected_item.row += 1; );
} }
} } 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;
}
}
project.was_moving = moved; project.was_moving = moved;
} }

View file

@ -3,26 +3,29 @@ use eframe::egui;
use crate::panels; use crate::panels;
pub struct ProjectItemWindow { pub struct ProjectItemWindow {
pub visible: bool, pub visible: bool,
} }
impl ProjectItemWindow { impl ProjectItemWindow {
pub fn new() -> Self { pub fn new() -> Self {
Self { visible: false } Self { visible: false }
} }
pub fn show(&mut self, ctx: &egui::Context, item: &mut panels::Item) { pub fn show(&mut self, ctx: &egui::Context, item: &mut panels::Item) {
let mut visible = self.visible; let mut visible = self.visible;
egui::Window::new("Project Item") egui::Window::new("Item")
.open(&mut visible) .open(&mut visible)
.vscroll(true) .vscroll(true)
.hscroll(true) .hscroll(true)
.show(ctx, |ui| self.ui(ui, item)); .show(ctx, |ui| self.ui(ui, item));
self.visible = self.visible && visible; self.visible = self.visible && visible;
} }
fn ui(&mut self, ui: &mut egui::Ui, item: &mut panels::Item) { fn ui(&mut self, ui: &mut egui::Ui, item: &mut panels::Item) {
ui.set_min_width(250.0); ui.set_min_width(250.0);
ui.label(item.name.clone()); ui.set_min_height(250.0);
} ui.add(egui::TextEdit::singleline(&mut item.name).desired_width(f32::INFINITY));
ui.separator();
ui.add_sized(ui.available_size(), egui::TextEdit::multiline(&mut item.description));
}
} }

View file

@ -1,36 +1,42 @@
use eframe::egui; use eframe::egui;
pub struct ShortcutsWindow { pub struct ShortcutsWindow {
pub visible: bool, pub visible: bool,
} }
impl ShortcutsWindow { impl ShortcutsWindow {
pub fn new() -> Self { pub fn new() -> Self {
Self { visible: false } Self { visible: false }
} }
pub fn show(&mut self, ctx: &egui::Context) { pub fn show(&mut self, ctx: &egui::Context) {
let mut visible = self.visible; let mut visible = self.visible;
egui::Window::new("Shortcuts") egui::Window::new("Shortcuts")
.open(&mut visible) .open(&mut visible)
.vscroll(true) .vscroll(true)
.hscroll(true) .hscroll(true)
.show(ctx, |ui| self.ui(ui)); .show(ctx, |ui| self.ui(ui));
self.visible = self.visible && visible; self.visible = self.visible && visible;
} }
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
ui.set_min_width(250.0); ui.set_min_width(250.0);
ui.label("Ctrl+S : save file"); ui.label("Ctrl+S : save file");
ui.label("Ctrl+Shift+S : save file as"); ui.label("Ctrl+Shift+S : save file as");
ui.label("Ctrl+R : reload file"); ui.label("Ctrl+R : reload file");
ui.separator(); ui.separator();
ui.label("Ctrl+F : open search window"); ui.label("Ctrl+F : open search window");
ui.separator(); ui.separator();
ui.label("Ctrl+Z : undo"); ui.label("Ctrl+Z : undo");
ui.label("Ctrl+Y : redo"); ui.label("Ctrl+Y : redo");
ui.label("Tab on selection : add indent of selection"); ui.label("Tab on selection : add indent of selection");
ui.label("Shift+Tab on selection : remove indent of selection"); ui.label("Shift+Tab on selection : remove indent of selection");
ui.label("Ctrl+E : comment selection"); ui.label("Ctrl+E : comment selection");
} ui.separator();
ui.label("Alt+Arrows : move between tabs");
ui.separator();
ui.label("Enter (project_mode) : edit item");
ui.label("Arrows (project_mode) : change selected item");
ui.label("Shift+Arrows (project_mode) : move selected item");
}
} }