Compare commits

..

10 commits

Author SHA1 Message Date
WanderingPenwing 23d81bf847 fmt 2024-08-07 11:40:44 +02:00
WanderingPenwing 1febc424a2 fixed project mode crash 2024-07-27 23:17:42 +02:00
WanderingPenwing acc2c27770 fixed dragging tab disappear if mouse too far 2024-07-25 10:43:51 +02:00
WanderingPenwing c508a18941 draggable tabs 2024-07-24 11:26:37 +02:00
WanderingPenwing f9a712fea4 better last tab closing behaviour 2024-07-23 21:18:36 +02:00
WanderingPenwing f86a8ab094 modified workflow compiled tar gz name 2024-07-22 22:51:03 +02:00
WanderingPenwing 85b2b587c1 back to working 1.4 2024-07-22 22:49:05 +02:00
WanderingPenwing 1d13e429ff removed custom build.rs 2024-07-22 20:36:00 +02:00
WanderingPenwing d3ed74d3b2 dependency check in workflow 2024-07-22 20:28:06 +02:00
WanderingPenwing d0f8c4d7fd removed tinyfiledialog from cargo toml 2024-07-22 20:17:35 +02:00
14 changed files with 1796 additions and 1591 deletions

View file

@ -1,59 +0,0 @@
permissions:
contents: write
actions: read
checks: write
deployments: write
issues: write
packages: write
pull-requests: write
statuses: write
on:
release:
types: [created]
jobs:
release:
name: release ${{ matrix.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
# Temporarily disabling Windows compilation
# - x86_64-pc-windows-gnu
- x86_64-unknown-linux-musl
steps:
- uses: actions/checkout@v2
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Install target
run: rustup target add ${{ matrix.target }}
- name: Check Rust target installation
run: rustup target list --installed
- name: Compile the app
run: |
echo "Compiling for target: ${{ matrix.target }}"
cargo build --release --target ${{ matrix.target }}
- name: Create tarball
run: |
release_tag=${{ github.event.release.tag_name }}
tar -czvf calcifer_v${release_tag}.tar.gz -C target/${{ matrix.target }}/release calcifer
- name: Upload release assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: calcifer_v${{ github.event.release.tag_name }}.tar.gz
asset_name: calcifer_v${{ github.event.release.tag_name }}.tar.gz
asset_content_type: application/gzip

View file

@ -1 +1 @@
{"categories":[{"name":"to do","content":[{"name":"update workflow .yml","description":"make a workflow compiling the calcifer and put the linux in calcifer-{version}\nand the windows in calcifer_windows_{version}\n\nupdate nix\nupdate jiji","id":5}]},{"name":"in progress","content":[{"name":"export copy paste fix","description":"// Hello there","id":1}]},{"name":"done","content":[{"name":"move .project file","description":"// Hello there","id":4},{"name":"move config","description":"config from .calcifer/save.json\nto .config/calcifer/state.json","id":1},{"name":"add id to textarea per tab","description":"to improve undo, make each code area of each tab have a unique id (no more undo into another tab)","id":1},{"name":"file tree id ?","description":"// Hello there","id":1},{"name":"open dir in tree ?","description":"// Hello there","id":2},{"name":"fix tab title","description":"// Hello there","id":2}]},{"name":"+","content":[]}]} {"categories":[{"name":"to do","content":[{"name":"update workflow .yml","description":"make a workflow compiling the calcifer and put the linux in calcifer-{version}\nand the windows in calcifer_windows_{version}\n\nupdate nix\nupdate jiji","id":5},{"name":"draggable item for project mode","description":"// Hello there","id":2}]},{"name":"in progress","content":[]},{"name":"done","content":[{"name":"move .project file","description":"// Hello there","id":4},{"name":"move config","description":"config from .calcifer/save.json\nto .config/calcifer/state.json","id":1},{"name":"add id to textarea per tab","description":"to improve undo, make each code area of each tab have a unique id (no more undo into another tab)","id":1},{"name":"file tree id ?","description":"// Hello there","id":1},{"name":"open dir in tree ?","description":"// Hello there","id":2},{"name":"fix tab title","description":"// Hello there","id":2},{"name":"when closing last tab","description":"close tab and THEN close calcifer (to save no tab in save.json)","id":1},{"name":"draggable tabs","description":"// Hello there","id":2},{"name":"repair build.rs","description":"// Hello there","id":1},{"name":"export copy paste fix","description":"// Hello there","id":1}]},{"name":"bug","content":[{"name":"ctrl f ","description":"I had crash when going up in the selection","id":1}]},{"name":"+","content":[]}]}

View file

@ -9,7 +9,7 @@ mkShell {
libXi libXi
pkg-config pkg-config
] ++ [ ] ++ [
cargo #cargo
rustc rustc
atk atk
gdk-pixbuf gdk-pixbuf
@ -19,12 +19,12 @@ mkShell {
libGLU libGLU
libxkbcommon libxkbcommon
gtk3-x11 gtk3-x11
gnome.zenity #gnome.zenity
]; ];
buildInputs = [ buildInputs = [
latest.rustChannels.stable.rust latest.rustChannels.stable.rust
xorg.libX11 xorg.libX11
wayland # wayland
libxkbcommon libxkbcommon
]; ];

View file

@ -1,6 +1,6 @@
use eframe::egui; use eframe::egui;
use egui::Color32; use egui::Color32;
use std::{cmp::min, fs, path::Path, path::PathBuf}; use std::{cmp::max, cmp::min, fs, path::Path, path::PathBuf};
use crate::core; use crate::core;
use crate::editor::themes::DEFAULT_THEMES; use crate::editor::themes::DEFAULT_THEMES;
@ -45,22 +45,23 @@ impl Calcifer {
pub fn save_tab_as(&self) -> Option<PathBuf> { pub fn save_tab_as(&self) -> Option<PathBuf> {
let default_path = self.home.join("untitled"); let default_path = self.home.join("untitled");
let save_path = if self.tabs[self.selected_tab].path.file_name().map_or(true, |name| name.to_string_lossy() == "untitled") let save_path = if self.tabs[self.selected_tab]
.path
.file_name()
.map_or(true, |name| name.to_string_lossy() == "untitled")
{ {
default_path.to_string_lossy() default_path.to_string_lossy()
} else { } else {
self.tabs[self.selected_tab].path.to_string_lossy() self.tabs[self.selected_tab].path.to_string_lossy()
}; };
println!("app : tried to open dialog at {}", save_path); if let Some(path_string) = tinyfiledialogs::save_file_dialog("Save as", &save_path) {
// if let Some(path_string) = tinyfiledialogs::save_file_dialog("Save as", &save_path) let path = PathBuf::from(path_string);
// { if let Err(err) = fs::write(&path, &self.tabs[self.selected_tab].code) {
// let path = PathBuf::from(path_string); eprintln!("Error writing file: {}", err);
// if let Err(err) = fs::write(&path, &self.tabs[self.selected_tab].code) { return None;
// eprintln!("Error writing file: {}", err); }
// return None; return Some(path);
// } }
// return Some(path);
// }
None None
} }
@ -156,8 +157,10 @@ impl Calcifer {
pub fn delete_tab(&mut self, index: usize) { pub fn delete_tab(&mut self, index: usize) {
self.tabs.remove(index); self.tabs.remove(index);
if self.tabs.len() != 0 {
self.selected_tab = min(index, self.tabs.len() - 1); self.selected_tab = min(index, self.tabs.len() - 1);
} }
}
pub fn toggle(&self, ui: &mut egui::Ui, display: bool, title: &str) -> bool { pub fn toggle(&self, ui: &mut egui::Ui, display: bool, title: &str) -> bool {
let bg_color: Color32; let bg_color: Color32;
@ -237,6 +240,10 @@ impl Calcifer {
false false
} }
pub fn tab_area_size(&self) -> usize {
max(6, self.tabs.len() + 1)
}
} }
#[allow(clippy::unnecessary_lazy_evaluations)] #[allow(clippy::unnecessary_lazy_evaluations)]

View file

@ -1,5 +1,7 @@
use eframe::egui; use eframe::egui;
use image::GenericImageView; use image::GenericImageView;
use serde::Deserialize;
use serde::Serialize;
use std::{ use std::{
error::Error, error::Error,
fs, fs,
@ -7,8 +9,6 @@ use std::{
io::Write, io::Write,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use serde::Serialize;
use serde::Deserialize;
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
pub struct AppState { pub struct AppState {

View file

@ -1,7 +1,7 @@
use eframe::egui; use eframe::egui;
use egui::{text::CCursor, text_edit::CCursorRange, Rangef}; use egui::{text::CCursor, text_edit::CCursorRange, Rangef};
use egui_extras::{Size, StripBuilder}; use egui_extras::{Size, StripBuilder};
use std::{cmp::max, env, ffi::OsStr, path::Component, path::Path, path::PathBuf}; use std::{cmp::max, cmp::min, env, ffi::OsStr, path::Component, path::Path, path::PathBuf};
use crate::core; use crate::core;
use crate::editor; use crate::editor;
@ -22,23 +22,37 @@ impl Calcifer {
.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() {
println!("ui : open file, wip"); if let Some(path_string) = tinyfiledialogs::open_file_dialog(
// if let Some(path_string) = tinyfiledialogs::open_file_dialog("Open File", &self.home.to_string_lossy(), None) "Open File",
// { &self.home.to_string_lossy(),
// self.open_file(Some(&Path::new(&path_string))); None,
// } ) {
self.open_file(Some(&Path::new(&path_string)));
}
} }
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, "🖵");
let toggle_terminal = self.toggle(ui, self.terminal_visible, "🖵");
if toggle_terminal && !self.terminal_visible {
let mut path = self.tabs[self.selected_tab].path.clone();
path.pop();
panels::send_command(format!("cd {}", path.display()));
}
self.terminal_visible = toggle_terminal;
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].language == PROJECT_EXTENSION { if self.tabs[self.selected_tab].language == PROJECT_EXTENSION {
@ -197,8 +211,8 @@ impl Calcifer {
0.0, 0.0,
core::hex_str_to_color(self.theme.bg), core::hex_str_to_color(self.theme.bg),
); );
StripBuilder::new(ui) let response = StripBuilder::new(ui)
.sizes(Size::remainder(), max(6, self.tabs.len() + 1)) .sizes(Size::remainder(), self.tab_area_size())
.sense(egui::Sense::click()) .sense(egui::Sense::click())
.horizontal(|mut strip| { .horizontal(|mut strip| {
for (index, tab) in self.tabs.clone().iter().enumerate() { for (index, tab) in self.tabs.clone().iter().enumerate() {
@ -227,24 +241,18 @@ impl Calcifer {
.clicked() .clicked()
&& !self.close_tab_confirm.visible && !self.close_tab_confirm.visible
{ {
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 {
egui::Context::send_viewport_cmd(
ctx,
egui::ViewportCommand::Close,
);
}
} }
ui.with_layout( ui.with_layout(
egui::Layout::left_to_right(egui::Align::TOP), egui::Layout::left_to_right(egui::Align::TOP),
|ui| { |ui| {
if ui.add( if ui
.add(
egui::Label::new( egui::Label::new(
egui::RichText::new(format!( egui::RichText::new(format!(
" {}{}", " {}{}",
@ -256,8 +264,14 @@ impl Calcifer {
.truncate(true) .truncate(true)
.sense(egui::Sense::click()), .sense(egui::Sense::click()),
) )
.clicked() || .clicked()
ui.add_sized(ui.available_size(), egui::Label::new("").sense(egui::Sense::click())).clicked() || ui
.add_sized(
ui.available_size(),
egui::Label::new("")
.sense(egui::Sense::click()),
)
.clicked()
{ {
self.selected_tab = index; self.selected_tab = index;
} }
@ -269,30 +283,32 @@ impl Calcifer {
} }
strip.cell(|ui| { strip.cell(|ui| {
if ui if ui
.add(egui::Label::new(" +").sense(egui::Sense::click())) .add(egui::Label::new(" ").sense(egui::Sense::click()))
.clicked() .clicked()
{ {
self.open_file(None); self.open_file(None);
} }
}); });
}); });
self.tab_rect = response.rect;
}); });
} }
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| { if self.selected_tab >= self.tabs.len() {
if ui return;
.add(egui::Button::new("open directory in terminal"))
.clicked()
{
let mut path = self.tabs[self.selected_tab].path.clone();
path.pop();
panels::send_command(format!("cd {}", path.display()));
} }
ui.horizontal(|ui| {
ui.style_mut().visuals.hyperlink_color =
core::hex_str_to_color(self.theme.comments);
if ui if ui
.add(egui::Button::new("expand tree")) .link(
self.tabs[self.selected_tab]
.path
.to_string_lossy()
.to_string(),
)
.clicked() .clicked()
{ {
let mut current_path = self.tabs[self.selected_tab].path.clone(); let mut current_path = self.tabs[self.selected_tab].path.clone();
@ -309,14 +325,6 @@ impl Calcifer {
self.tree_visible = true; self.tree_visible = true;
self.file_tree = None; self.file_tree = None;
} }
ui.label("Picked file:");
ui.monospace(
self.tabs[self.selected_tab]
.path
.to_string_lossy()
.to_string(),
);
}); });
ui.separator(); ui.separator();
@ -359,7 +367,7 @@ impl Calcifer {
&mut current_tab.scroll_offset.clone(), &mut current_tab.scroll_offset.clone(),
override_cursor.clone(), override_cursor.clone(),
); );
return return;
} }
CodeEditor::default() CodeEditor::default()
@ -386,6 +394,32 @@ impl Calcifer {
.update_from_code(current_tab.code.clone()); .update_from_code(current_tab.code.clone());
panels::draw_project(ui, self.theme, &mut self.project_content); panels::draw_project(ui, self.theme, &mut self.project_content);
self.project_content.selected_item.category = min(
self.project_content.categories.len() - 2,
self.project_content.selected_item.category,
);
while self.project_content.categories[self.project_content.selected_item.category]
.content
.is_empty()
&& self.project_content.selected_item.category > 0
{
self.project_content.selected_item.category -= 1;
}
if !self.project_content.categories[self.project_content.selected_item.category]
.content
.is_empty()
{
self.project_content.selected_item.row = min(
self.project_content.categories[self.project_content.selected_item.category]
.content
.len()
- 1,
self.project_content.selected_item.row,
);
} else {
self.project_content.selected_item.row = 0;
}
if self.project_content.item_window.visible { if self.project_content.item_window.visible {
if self.project_content.categories.len() > 1 if self.project_content.categories.len() > 1
&& !self.project_content.categories[self.project_content.selected_item.category] && !self.project_content.categories[self.project_content.selected_item.category]
@ -401,10 +435,16 @@ impl Calcifer {
if delete_item { if delete_item {
self.project_content.item_window.visible = false; self.project_content.item_window.visible = false;
self.project_content.categories self.project_content.categories[self.project_content.selected_item.category]
.content
.remove(self.project_content.selected_item.row);
if self.project_content.selected_item.row
>= self.project_content.categories
[self.project_content.selected_item.category] [self.project_content.selected_item.category]
.content.remove(self.project_content.selected_item.row); .content
if self.project_content.selected_item.row >= self.project_content.categories[self.project_content.selected_item.category].content.len() && self.project_content.selected_item.row > 0 { .len()
&& self.project_content.selected_item.row > 0
{
self.project_content.selected_item.row -= 1; self.project_content.selected_item.row -= 1;
} }
} }
@ -456,6 +496,120 @@ impl Calcifer {
self.handle_confirm(); self.handle_confirm();
} }
pub fn draw_mouse_drag(&mut self, ctx: &egui::Context) {
if ctx.input(|i| i.pointer.is_decidedly_dragging()) {
if let Some(pos) = ctx.input(|i| i.pointer.interact_pos()) {
match self.mouse_holder {
panels::MouseHolder::TabHolder(index) => {
let snapped_pos = egui::Pos2::new(
pos.x,
(self.tab_rect.max.y + self.tab_rect.min.y) / 2.0,
);
egui::Area::new(egui::Id::new("mouse_holder"))
.fixed_pos(snapped_pos)
.show(ctx, |ui| {
let (bg_color, text_color) = if self.selected_tab == index {
(
core::hex_str_to_color(self.theme.functions),
core::hex_str_to_color(self.theme.bg),
)
} else {
(
core::hex_str_to_color(self.theme.bg),
core::hex_str_to_color(self.theme.comments),
)
};
let rect = egui::Rect::from_center_size(
snapped_pos,
egui::Vec2::new(
(self.tab_rect.max.x - self.tab_rect.min.x)
/ usize_to_f32(self.tab_area_size()),
self.tab_rect.max.y - self.tab_rect.min.y,
),
);
ui.painter().rect_filled(rect, 0.0, bg_color);
let unsaved_indicator =
if self.tabs[index].saved { "" } else { "~ " };
let _ = ui.put(
rect,
egui::Label::new(
egui::RichText::new(format!(
" {}{}",
unsaved_indicator,
self.tabs[index].get_name()
))
.color(text_color),
),
);
});
}
panels::MouseHolder::None => {
if self.tab_rect.distance_to_pos(pos) == 0.0 {
let hover_pos: f32 = (pos.x - self.tab_rect.min.x)
/ ((self.tab_rect.max.x - self.tab_rect.min.x)
/ usize_to_f32(self.tab_area_size()));
if let Some(index) = floor_f32(hover_pos) {
if index < self.tabs.len() {
self.mouse_holder = panels::MouseHolder::TabHolder(index);
}
}
}
}
}
}
return;
}
match self.mouse_holder {
panels::MouseHolder::TabHolder(initial_index) => {
if let Some(pos) = ctx.input(|i| i.pointer.interact_pos()) {
let snapped_pos =
egui::Pos2::new(pos.x, (self.tab_rect.max.y + self.tab_rect.min.y) / 2.0);
if self.tab_rect.distance_to_pos(snapped_pos) == 0.0 {
let hover_pos: f32 = (pos.x - self.tab_rect.min.x)
/ ((self.tab_rect.max.x - self.tab_rect.min.x)
/ usize_to_f32(self.tab_area_size()));
if let Some(final_index) = floor_f32(hover_pos) {
if final_index == initial_index {
return;
} else if final_index < initial_index {
self.tabs
.insert(final_index, self.tabs[initial_index].clone());
self.tabs.remove(initial_index + 1);
} else {
self.tabs
.insert(final_index + 1, self.tabs[initial_index].clone());
self.tabs.remove(initial_index);
}
if self.selected_tab == initial_index {
self.selected_tab = final_index;
} else if self.selected_tab < initial_index
&& self.selected_tab >= final_index
{
self.selected_tab += 1;
} else if self.selected_tab > initial_index
&& self.selected_tab <= final_index
{
self.selected_tab -= 1;
}
}
}
}
}
panels::MouseHolder::None => {}
}
self.mouse_holder = panels::MouseHolder::None;
}
} }
fn to_syntax(language: &str) -> Syntax { fn to_syntax(language: &str) -> Syntax {
@ -488,3 +642,21 @@ pub fn format_path(path: &Path) -> String {
.join("/") .join("/")
) )
} }
fn usize_to_f32(value: usize) -> f32 {
const MAX_F32: f32 = f32::MAX;
if value as f64 > MAX_F32 as f64 {
MAX_F32
} else {
value as f32
}
}
fn floor_f32(value: f32) -> Option<usize> {
if value.is_nan() || value < 0.0 || value > usize::MAX as f32 {
None
} else {
Some(value.floor() as usize)
}
}

View file

@ -9,14 +9,79 @@ impl Syntax {
comment: "//", comment: "//",
comment_multiline: ["/*", "*/"], comment_multiline: ["/*", "*/"],
keywords: BTreeSet::from([ keywords: BTreeSet::from([
"&&", "||", "!", "let", "var", "abstract", "arguments", "await", "break", "case", "catch", "class", "const", "continue", "&&",
"debugger", "default", "delete", "do", "else", "enum", "eval", "export", "extends", "final", "finally", "for", "function", "||",
"goto", "if", "implements", "import", "in", "instanceof", "interface", "let", "native", "new", "package", "private", "protected", "!",
"public", "return", "static", "super", "switch", "synchronized", "this","throw", "throws", "transient", "try", "typeof", "let",
"var", "volatile", "while", "with", "yield", "var",
"abstract",
"arguments",
"await",
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"eval",
"export",
"extends",
"final",
"finally",
"for",
"function",
"goto",
"if",
"implements",
"import",
"in",
"instanceof",
"interface",
"let",
"native",
"new",
"package",
"private",
"protected",
"public",
"return",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"transient",
"try",
"typeof",
"var",
"volatile",
"while",
"with",
"yield",
]), ]),
types: BTreeSet::from([ types: BTreeSet::from([
"Boolean", "Number", "BigInt", "Undefined", "Null", "String", "Symbol", "byte", "char", "float", "int", "long", "short", "void", "Boolean",
"Number",
"BigInt",
"Undefined",
"Null",
"String",
"Symbol",
"byte",
"char",
"float",
"int",
"long",
"short",
"void",
]), ]),
special: BTreeSet::from(["false", "null", "true"]), special: BTreeSet::from(["false", "null", "true"]),
} }

View file

@ -4,9 +4,9 @@ use egui::{
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::env; use std::env;
use std::time::Duration; use std::time::Duration;
use std::{ops::Range, path::PathBuf, sync::Arc, thread, time};
mod core; mod core;
mod editor; mod editor;
@ -25,7 +25,13 @@ 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+tray",
"tabs",
"content",
"windows",
]; ];
const ZOOM_FACTOR: f32 = 1.1; const ZOOM_FACTOR: f32 = 1.1;
const MAX_FPS: f32 = 30.0; const MAX_FPS: f32 = 30.0;
@ -63,6 +69,8 @@ fn main() -> Result<(), eframe::Error> {
None None
}; };
//panels::send_command("export RUSTFLAGS=--cfg=web_sys_unstable_apis".to_string());
eframe::run_native( eframe::run_native(
&format!("Calcifer{}", TITLE), &format!("Calcifer{}", TITLE),
options, options,
@ -76,6 +84,8 @@ struct Calcifer {
selected_tab: usize, selected_tab: usize,
tabs: Vec<panels::Tab>, tabs: Vec<panels::Tab>,
tab_rect: egui::Rect,
mouse_holder: panels::MouseHolder,
command: String, command: String,
command_history: Vec<panels::CommandEntry>, command_history: Vec<panels::CommandEntry>,
@ -117,6 +127,8 @@ impl Default for Calcifer {
selected_tab: 0, selected_tab: 0,
tabs: vec![panels::Tab::default()], tabs: vec![panels::Tab::default()],
tab_rect: egui::Rect::EVERYTHING,
mouse_holder: panels::MouseHolder::None,
command: String::new(), command: String::new(),
command_history: Vec::new(), command_history: Vec::new(),
@ -201,7 +213,8 @@ impl eframe::App for Calcifer {
} }
} }
if ctx.input(|i| i.key_pressed(egui::Key::Enter)) && ctx.memory(|m| m.focus() == None) if ctx.input(|i| i.key_pressed(egui::Key::Enter))
&& ctx.memory(|m| m.focus() == None)
&& self.tabs[self.selected_tab].language == PROJECT_EXTENSION && self.tabs[self.selected_tab].language == PROJECT_EXTENSION
{ {
self.project_content.item_window.visible = true; self.project_content.item_window.visible = true;
@ -267,6 +280,11 @@ impl eframe::App for Calcifer {
} }
} }
if self.tabs.len() == 0 {
egui::Context::send_viewport_cmd(ctx, egui::ViewportCommand::Close);
return;
}
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();
@ -297,11 +315,15 @@ impl eframe::App for Calcifer {
watch = time::Instant::now(); watch = time::Instant::now();
self.draw_windows(ctx); self.draw_windows(ctx);
self.draw_mouse_drag(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;
if self.running_command { if self.running_command {
egui::Context::request_repaint_after(ctx, Duration::from_secs_f32(RUNNING_COMMAND_REFRESH_DELAY)); egui::Context::request_repaint_after(
ctx,
Duration::from_secs_f32(RUNNING_COMMAND_REFRESH_DELAY),
);
} }
} }

View file

@ -1,14 +1,14 @@
use std::hash::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
ffi::OsStr, ffi::OsStr,
fs, io, fs, io,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use std::hash::DefaultHasher;
use std::time::UNIX_EPOCH;
use std::time::SystemTime;
use std::hash::Hasher;
use std::hash::Hash;
//use crate::ALLOWED_FILE_EXTENSIONS; //use crate::ALLOWED_FILE_EXTENSIONS;
@ -42,7 +42,10 @@ impl FileEntry {
} }
fn generate_unique_id(path: PathBuf) -> String { fn generate_unique_id(path: PathBuf) -> String {
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
now.hash(&mut hasher); now.hash(&mut hasher);

View file

@ -1,7 +1,6 @@
use eframe::egui; use eframe::egui;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
cmp::min,
cmp::max, cmp::max,
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
@ -124,7 +123,9 @@ fn get_id() -> usize {
} }
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(MAX_PROJECT_COLUMNS, project.categories.len() + 1) , |uis| { ui.columns(
max(MAX_PROJECT_COLUMNS, project.categories.len() + 1),
|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];
@ -152,7 +153,8 @@ pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project)
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)),
@ -161,7 +163,10 @@ pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project)
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 {
@ -171,22 +176,15 @@ pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project)
} }
} }
} }
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 category.name != "+" && ui.add(egui::Button::new("+")).clicked() { if category.name != "+" && ui.add(egui::Button::new("+")).clicked() {
project.categories[category_index] project.categories[category_index]
.content .content
.push(Item::new("item")); .push(Item::new("item"));
} }
// if category.name != "+" {
// if ui.add(egui::Button::new("+")).clicked() {
// project.categories[category_index]
// .content
// .push(Item::new("item"));
// }
// }
} }
}); },
);
let mut moved = false; let mut moved = false;
let category = project.selected_item.category; let category = project.selected_item.category;
@ -238,23 +236,15 @@ pub fn draw_project(ui: &mut egui::Ui, theme: ColorTheme, project: &mut Project)
&& project.selected_item.category > 0 && project.selected_item.category > 0
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving && project.categories[category - 1].content.len() > 0 {
project.selected_item.category -= 1; 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)) } else if ui.input(|i| i.key_pressed(egui::Key::ArrowRight))
&& project.selected_item.category < project.categories.len() - 2 && project.selected_item.category < project.categories.len() - 2
{ {
moved = true; moved = true;
if !project.was_moving { if !project.was_moving && project.categories[category + 1].content.len() > 0 {
project.selected_item.category += 1; 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 { } else if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) && project.selected_item.row > 0 {
moved = true; moved = true;

View file

@ -61,6 +61,11 @@ impl Tab {
} }
} }
pub enum MouseHolder {
TabHolder(usize),
None,
}
fn read_file_contents(path: &Path) -> String { fn read_file_contents(path: &Path) -> String {
let error_type = "reading file"; let error_type = "reading file";
read_to_string(path) read_to_string(path)

View file

@ -22,10 +22,10 @@ impl ProjectItemWindow {
if let Some(response) = maybe_response { if let Some(response) = maybe_response {
if let Some(delete_option) = response.inner { if let Some(delete_option) = response.inner {
return delete_option return delete_option;
} }
} }
return false return false;
} }
fn ui(&mut self, ui: &mut egui::Ui, item: &mut panels::Item) -> bool { fn ui(&mut self, ui: &mut egui::Ui, item: &mut panels::Item) -> bool {
@ -43,6 +43,6 @@ impl ProjectItemWindow {
ui.available_size(), ui.available_size(),
egui::TextEdit::multiline(&mut item.description), egui::TextEdit::multiline(&mut item.description),
); );
return delete_item.clone() return delete_item.clone();
} }
} }