master #7
@ -2,35 +2,32 @@ use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn compute_content_hash(content: &str, hasher: &mut DefaultHasher) -> u64 {
|
||||
content.hash(hasher);
|
||||
hasher.finish()
|
||||
pub fn compute_content_hash(content: &str) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
content.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
hash
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tab {
|
||||
pub content: String,
|
||||
pub original_content_hash: u64,
|
||||
pub last_content_hash: u64,
|
||||
pub file_path: Option<PathBuf>,
|
||||
pub is_modified: bool,
|
||||
pub title: String,
|
||||
pub hasher: DefaultHasher,
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new_empty(tab_number: usize) -> Self {
|
||||
let content = String::new();
|
||||
let mut hasher = DefaultHasher::new();
|
||||
let hash = compute_content_hash(&content, &mut hasher);
|
||||
let hash = compute_content_hash(&content);
|
||||
Self {
|
||||
original_content_hash: hash,
|
||||
last_content_hash: hash,
|
||||
content,
|
||||
file_path: None,
|
||||
is_modified: false,
|
||||
title: format!("new_{tab_number}"),
|
||||
hasher,
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,19 +35,16 @@ impl Tab {
|
||||
let title = file_path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("Untitled")
|
||||
.unwrap_or("UNKNOWN")
|
||||
.to_string();
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
let hash = compute_content_hash(&content, &mut hasher);
|
||||
let hash = compute_content_hash(&content);
|
||||
Self {
|
||||
original_content_hash: hash,
|
||||
last_content_hash: hash,
|
||||
content,
|
||||
file_path: Some(file_path),
|
||||
is_modified: false,
|
||||
title,
|
||||
hasher,
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,15 +57,13 @@ impl Tab {
|
||||
if self.title.starts_with("new_") {
|
||||
self.is_modified = !self.content.is_empty();
|
||||
} else {
|
||||
let current_hash = compute_content_hash(&self.content, &mut self.hasher);
|
||||
self.is_modified = current_hash != self.last_content_hash;
|
||||
self.last_content_hash = current_hash;
|
||||
let current_hash = compute_content_hash(&self.content);
|
||||
self.is_modified = current_hash != self.original_content_hash;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_as_saved(&mut self) {
|
||||
self.original_content_hash = compute_content_hash(&self.content, &mut self.hasher);
|
||||
self.last_content_hash = self.original_content_hash;
|
||||
self.original_content_hash = compute_content_hash(&self.content);
|
||||
self.is_modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,11 +248,6 @@ pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::R
|
||||
|
||||
app.previous_content = content.to_owned();
|
||||
app.previous_cursor_char_index = current_cursor_pos;
|
||||
|
||||
if let Some(active_tab) = app.get_active_tab_mut() {
|
||||
active_tab.last_content_hash =
|
||||
crate::app::tab::compute_content_hash(&active_tab.content, &mut active_tab.hasher);
|
||||
}
|
||||
}
|
||||
|
||||
if app.font_settings_changed || app.text_needs_processing {
|
||||
|
||||
@ -276,21 +276,15 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) {
|
||||
|
||||
if app.hide_tab_bar {
|
||||
let tab_title = if let Some(tab) = app.get_active_tab() {
|
||||
tab.title.to_owned()
|
||||
tab.get_display_title()
|
||||
} else {
|
||||
let empty_tab = crate::app::tab::Tab::new_empty(1);
|
||||
empty_tab.title.to_owned()
|
||||
empty_tab.get_display_title()
|
||||
};
|
||||
|
||||
let window_width = ctx.screen_rect().width();
|
||||
let font_id = ui.style().text_styles[&egui::TextStyle::Body].to_owned();
|
||||
|
||||
let tab_title = if app.get_active_tab().is_some_and(|tab| tab.is_modified) {
|
||||
format!("{tab_title}*")
|
||||
} else {
|
||||
tab_title
|
||||
};
|
||||
|
||||
let text_galley = ui.fonts(|fonts| {
|
||||
fonts.layout_job(egui::text::LayoutJob::simple_singleline(
|
||||
tab_title,
|
||||
|
||||
@ -3,86 +3,92 @@ use eframe::egui::{self, Frame};
|
||||
|
||||
pub(crate) fn tab_bar(app: &mut TextEditor, ctx: &egui::Context) {
|
||||
let frame = Frame::NONE.fill(ctx.style().visuals.panel_fill);
|
||||
let response = egui::TopBottomPanel::top("tab_bar")
|
||||
let tab_bar = egui::TopBottomPanel::top("tab_bar")
|
||||
.frame(frame)
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let mut tab_to_close_unmodified = None;
|
||||
let mut tab_to_close_modified = None;
|
||||
let mut tab_to_switch = None;
|
||||
let mut add_new_tab = false;
|
||||
egui::ScrollArea::horizontal()
|
||||
.auto_shrink([false, true])
|
||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||
.scroll_source(egui::scroll_area::ScrollSource::DRAG)
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let mut tab_to_close_unmodified = None;
|
||||
let mut tab_to_close_modified = None;
|
||||
let mut tab_to_switch = None;
|
||||
let mut add_new_tab = false;
|
||||
|
||||
let tabs_len = app.tabs.len();
|
||||
let active_tab_index = app.active_tab_index;
|
||||
let tabs_len = app.tabs.len();
|
||||
let active_tab_index = app.active_tab_index;
|
||||
|
||||
let tabs_info: Vec<(String, bool)> = app
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab| (tab.get_display_title(), tab.is_modified))
|
||||
.collect();
|
||||
let tabs_info: Vec<(String, bool)> = app
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab| (tab.get_display_title(), tab.is_modified))
|
||||
.collect();
|
||||
|
||||
for (i, (title, is_modified)) in tabs_info.iter().enumerate() {
|
||||
let is_active = i == active_tab_index;
|
||||
for (i, (title, is_modified)) in tabs_info.iter().enumerate() {
|
||||
let is_active = i == active_tab_index;
|
||||
|
||||
let mut label_text = if is_active {
|
||||
egui::RichText::new(title).strong()
|
||||
} else {
|
||||
egui::RichText::new(title).color(ui.visuals().weak_text_color())
|
||||
};
|
||||
let mut label_text = if is_active {
|
||||
egui::RichText::new(title).strong()
|
||||
} else {
|
||||
egui::RichText::new(title).color(ui.visuals().weak_text_color())
|
||||
};
|
||||
|
||||
if *is_modified {
|
||||
label_text = label_text.italics();
|
||||
}
|
||||
if *is_modified {
|
||||
label_text = label_text.italics();
|
||||
}
|
||||
|
||||
let tab_response =
|
||||
ui.add(egui::Label::new(label_text).sense(egui::Sense::click()));
|
||||
if tab_response.clicked() {
|
||||
tab_to_switch = Some(i);
|
||||
}
|
||||
let tab_response = ui.add(egui::Label::new(label_text).selectable(false).sense(egui::Sense::click()));
|
||||
if tab_response.clicked() {
|
||||
tab_to_switch = Some(i);
|
||||
}
|
||||
|
||||
if tabs_len > 1 {
|
||||
let visuals = ui.visuals();
|
||||
let close_button = egui::Button::new("×")
|
||||
.small()
|
||||
.fill(visuals.panel_fill)
|
||||
.stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0)));
|
||||
let close_response = ui.add(close_button);
|
||||
if close_response.clicked() {
|
||||
if *is_modified {
|
||||
tab_to_close_modified = Some(i);
|
||||
} else {
|
||||
tab_to_close_unmodified = Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
if tabs_len > 1 {
|
||||
let visuals = ui.visuals();
|
||||
let close_button = egui::Button::new("×")
|
||||
let add_button = egui::Button::new("+")
|
||||
.small()
|
||||
.fill(visuals.panel_fill)
|
||||
.stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0)));
|
||||
let close_response = ui.add(close_button);
|
||||
if close_response.clicked() {
|
||||
if *is_modified {
|
||||
tab_to_close_modified = Some(i);
|
||||
} else {
|
||||
tab_to_close_unmodified = Some(i);
|
||||
}
|
||||
if ui.add(add_button).clicked() {
|
||||
add_new_tab = true;
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
let visuals = ui.visuals();
|
||||
let add_button = egui::Button::new("+")
|
||||
.small()
|
||||
.fill(visuals.panel_fill)
|
||||
.stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0)));
|
||||
if ui.add(add_button).clicked() {
|
||||
add_new_tab = true;
|
||||
}
|
||||
|
||||
if let Some(tab_index) = tab_to_switch {
|
||||
app.switch_to_tab(tab_index);
|
||||
}
|
||||
if let Some(tab_index) = tab_to_close_unmodified {
|
||||
app.close_tab(tab_index);
|
||||
}
|
||||
if let Some(tab_index) = tab_to_close_modified {
|
||||
app.switch_to_tab(tab_index);
|
||||
app.pending_unsaved_action = Some(UnsavedAction::CloseTab(tab_index));
|
||||
}
|
||||
if add_new_tab {
|
||||
app.add_new_tab();
|
||||
}
|
||||
});
|
||||
if let Some(tab_index) = tab_to_switch {
|
||||
app.switch_to_tab(tab_index);
|
||||
}
|
||||
if let Some(tab_index) = tab_to_close_unmodified {
|
||||
app.close_tab(tab_index);
|
||||
}
|
||||
if let Some(tab_index) = tab_to_close_modified {
|
||||
app.switch_to_tab(tab_index);
|
||||
app.pending_unsaved_action = Some(UnsavedAction::CloseTab(tab_index));
|
||||
}
|
||||
if add_new_tab {
|
||||
app.add_new_tab();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.tab_bar_rect = Some(response.response.rect);
|
||||
app.tab_bar_rect = Some(tab_bar.response.rect);
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user