feat: refactor to make sure new tabs are added to end of the tab list always
This commit is contained in:
+60
-69
@@ -1,6 +1,5 @@
|
|||||||
mod file;
|
mod file;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use iced::padding::bottom;
|
use iced::padding::bottom;
|
||||||
@@ -11,7 +10,6 @@ use iced::{Background, Border, Element, border};
|
|||||||
use iced::{Font, Length, Theme};
|
use iced::{Font, Length, Theme};
|
||||||
use iced::{keyboard, window};
|
use iced::{keyboard, window};
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
const CUSTOM_FONT: Font = Font::with_name("CaskaydiaCove Nerd Font Mono");
|
const CUSTOM_FONT: Font = Font::with_name("CaskaydiaCove Nerd Font Mono");
|
||||||
const DEFAULT_EDITOR_FONT_SIZE: u32 = 16;
|
const DEFAULT_EDITOR_FONT_SIZE: u32 = 16;
|
||||||
@@ -40,7 +38,6 @@ const MIN_EDITOR_FONT_SIZE: u32 = 12;
|
|||||||
// TODO: Opening another window
|
// TODO: Opening another window
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
id: Uuid,
|
|
||||||
content: text_editor::Content,
|
content: text_editor::Content,
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
@@ -48,7 +45,6 @@ struct File {
|
|||||||
impl Default for File {
|
impl Default for File {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
File {
|
File {
|
||||||
id: Uuid::new_v4(),
|
|
||||||
content: text_editor::Content::new(),
|
content: text_editor::Content::new(),
|
||||||
path: None,
|
path: None,
|
||||||
}
|
}
|
||||||
@@ -63,8 +59,8 @@ impl Default for File {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct State {
|
struct State {
|
||||||
files: HashMap<Uuid, File>,
|
files: Vec<File>,
|
||||||
current_file: Uuid,
|
current_file: usize,
|
||||||
editor_font_size: u32,
|
editor_font_size: u32,
|
||||||
selected_file_action: Option<FileAction>,
|
selected_file_action: Option<FileAction>,
|
||||||
selected_view_action: Option<ViewAction>,
|
selected_view_action: Option<ViewAction>,
|
||||||
@@ -82,6 +78,7 @@ enum FileAction {
|
|||||||
Save,
|
Save,
|
||||||
SaveAs,
|
SaveAs,
|
||||||
Open,
|
Open,
|
||||||
|
Close(Option<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileAction {
|
impl FileAction {
|
||||||
@@ -90,6 +87,7 @@ impl FileAction {
|
|||||||
FileAction::Save,
|
FileAction::Save,
|
||||||
FileAction::SaveAs,
|
FileAction::SaveAs,
|
||||||
FileAction::Open,
|
FileAction::Open,
|
||||||
|
FileAction::Close(None),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,31 +98,32 @@ impl std::fmt::Display for FileAction {
|
|||||||
FileAction::Save => write!(f, "Save"),
|
FileAction::Save => write!(f, "Save"),
|
||||||
FileAction::SaveAs => write!(f, "Save as... "),
|
FileAction::SaveAs => write!(f, "Save as... "),
|
||||||
FileAction::Open => write!(f, "Open"),
|
FileAction::Open => write!(f, "Open"),
|
||||||
|
FileAction::Close(_) => write!(f, "Close"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum ViewAction {
|
enum ViewAction {
|
||||||
IncreaseFont,
|
Increase,
|
||||||
DecreaseFont,
|
Decrease,
|
||||||
ResetFont,
|
Reset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewAction {
|
impl ViewAction {
|
||||||
const ALL: &'static [ViewAction] = &[
|
const ALL: &'static [ViewAction] = &[
|
||||||
ViewAction::IncreaseFont,
|
ViewAction::Increase,
|
||||||
ViewAction::DecreaseFont,
|
ViewAction::Decrease,
|
||||||
ViewAction::ResetFont,
|
ViewAction::Reset,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ViewAction {
|
impl std::fmt::Display for ViewAction {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ViewAction::DecreaseFont => write!(f, "Decrease font"),
|
ViewAction::Decrease => write!(f, "Decrease font"),
|
||||||
ViewAction::IncreaseFont => write!(f, "Increase font"),
|
ViewAction::Increase => write!(f, "Increase font"),
|
||||||
ViewAction::ResetFont => write!(f, "Reset font"),
|
ViewAction::Reset => write!(f, "Reset font"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +133,7 @@ enum Message {
|
|||||||
Edit(text_editor::Action),
|
Edit(text_editor::Action),
|
||||||
FileActionSelected(FileAction),
|
FileActionSelected(FileAction),
|
||||||
ViewActionSelected(ViewAction),
|
ViewActionSelected(ViewAction),
|
||||||
SwitchTab(Uuid),
|
SwitchTab(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(_state: &State) -> Theme {
|
fn theme(_state: &State) -> Theme {
|
||||||
@@ -144,8 +143,8 @@ fn theme(_state: &State) -> Theme {
|
|||||||
fn view(state: &State) -> Element<'_, Message> {
|
fn view(state: &State) -> Element<'_, Message> {
|
||||||
let mut tab_row = row![];
|
let mut tab_row = row![];
|
||||||
|
|
||||||
for file in &state.files {
|
for (file_index, file) in state.files.iter().enumerate() {
|
||||||
let file_name = if let Some(p) = &file.1.path {
|
let file_name = if let Some(p) = &file.path {
|
||||||
p.file_name()
|
p.file_name()
|
||||||
.expect("unable to get file name")
|
.expect("unable to get file name")
|
||||||
.to_str()
|
.to_str()
|
||||||
@@ -154,13 +153,15 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
"New file"
|
"New file"
|
||||||
};
|
};
|
||||||
|
|
||||||
let button_text = text(file_name).wrapping(text::Wrapping::None);
|
let tab_button_text = text(file_name).wrapping(text::Wrapping::None);
|
||||||
|
let delete_button = button(text("x")).on_press(Message::FileActionSelected(
|
||||||
|
FileAction::Close(Some(file_index)),
|
||||||
|
));
|
||||||
|
|
||||||
let button = button(button_text)
|
let tab_button = button(row![tab_button_text, delete_button])
|
||||||
.style(|theme: &Theme, status| {
|
.style(move |theme: &Theme, status| {
|
||||||
let base = button::primary(theme, status);
|
let base = button::primary(theme, status);
|
||||||
let current_file = state.files.get(&state.current_file).expect("problem");
|
let is_focused = state.current_file == file_index;
|
||||||
let is_focused = current_file.id == file.0.clone();
|
|
||||||
let button_background = if is_focused {
|
let button_background = if is_focused {
|
||||||
base.background
|
base.background
|
||||||
} else {
|
} else {
|
||||||
@@ -176,9 +177,9 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
..base
|
..base
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on_press(Message::SwitchTab(file.0.clone()));
|
.on_press(Message::SwitchTab(file_index));
|
||||||
|
|
||||||
tab_row = tab_row.push(button);
|
tab_row = tab_row.push(tab_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabs = scrollable(container(tab_row).padding(bottom(10))).direction(
|
let tabs = scrollable(container(tab_row).padding(bottom(10))).direction(
|
||||||
@@ -201,7 +202,7 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
|
|
||||||
let action_bar = container(row![file_menu, view_menu].spacing(5));
|
let action_bar = container(row![file_menu, view_menu].spacing(5));
|
||||||
|
|
||||||
let current_file = state.files.get(&state.current_file).expect("problem");
|
let current_file = &state.files[state.current_file];
|
||||||
|
|
||||||
let editor = text_editor(¤t_file.content)
|
let editor = text_editor(¤t_file.content)
|
||||||
.size(state.editor_font_size)
|
.size(state.editor_font_size)
|
||||||
@@ -227,19 +228,19 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
|
|
||||||
if is_reset_font {
|
if is_reset_font {
|
||||||
return Some(Binding::Custom(Message::ViewActionSelected(
|
return Some(Binding::Custom(Message::ViewActionSelected(
|
||||||
ViewAction::ResetFont,
|
ViewAction::Reset,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_increase_font {
|
if is_increase_font {
|
||||||
return Some(Binding::Custom(Message::ViewActionSelected(
|
return Some(Binding::Custom(Message::ViewActionSelected(
|
||||||
ViewAction::IncreaseFont,
|
ViewAction::Increase,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_decrease_font {
|
if is_decrease_font {
|
||||||
return Some(Binding::Custom(Message::ViewActionSelected(
|
return Some(Binding::Custom(Message::ViewActionSelected(
|
||||||
ViewAction::DecreaseFont,
|
ViewAction::Decrease,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,8 +281,6 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
);
|
);
|
||||||
let cursor_text = text(cursor_display_text);
|
let cursor_text = text(cursor_display_text);
|
||||||
|
|
||||||
let current_file = state.files.get(&state.current_file).expect("problem");
|
|
||||||
|
|
||||||
let file_path_display_text = match ¤t_file.path {
|
let file_path_display_text = match ¤t_file.path {
|
||||||
Some(path) => path.to_string_lossy().to_string(),
|
Some(path) => path.to_string_lossy().to_string(),
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
@@ -299,7 +298,7 @@ fn view(state: &State) -> Element<'_, Message> {
|
|||||||
fn save_file(path: Option<PathBuf>, text: String) -> Option<PathBuf> {
|
fn save_file(path: Option<PathBuf>, text: String) -> Option<PathBuf> {
|
||||||
let mut save_path = path.clone();
|
let mut save_path = path.clone();
|
||||||
|
|
||||||
if path == None {
|
if path.is_none() {
|
||||||
save_path = FileDialog::new().set_directory("/").save_file();
|
save_path = FileDialog::new().set_directory("/").save_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +339,7 @@ fn open_file() -> (Option<PathBuf>, String) {
|
|||||||
fn update(state: &mut State, message: Message) {
|
fn update(state: &mut State, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::Edit(action) => {
|
Message::Edit(action) => {
|
||||||
let current_file = state.files.get_mut(&state.current_file).expect("problem");
|
let current_file = &mut state.files[state.current_file];
|
||||||
current_file.content.perform(action);
|
current_file.content.perform(action);
|
||||||
}
|
}
|
||||||
Message::FileActionSelected(action) => {
|
Message::FileActionSelected(action) => {
|
||||||
@@ -348,57 +347,51 @@ fn update(state: &mut State, message: Message) {
|
|||||||
|
|
||||||
match action {
|
match action {
|
||||||
FileAction::SaveAs => {
|
FileAction::SaveAs => {
|
||||||
let current_file = state.files.get_mut(&state.current_file).expect("problem");
|
let current_file = &mut state.files[state.current_file];
|
||||||
let path = save_file_as(current_file.content.text());
|
let path = save_file_as(current_file.content.text());
|
||||||
current_file.path = path;
|
current_file.path = path;
|
||||||
}
|
}
|
||||||
FileAction::Open => {
|
FileAction::Open => {
|
||||||
let (path, content) = open_file();
|
let (path, content) = open_file();
|
||||||
|
|
||||||
match &path {
|
if let Some(opened_path) = &path {
|
||||||
Some(opened_path) => {
|
for (file_index, file) in state.files.iter_mut().enumerate() {
|
||||||
// TODO: Chat thinks this is stupid
|
if let Some(existing_path) = &file.path
|
||||||
// and I agree. There is probably
|
&& opened_path == existing_path
|
||||||
// a way to have some sort of map
|
{
|
||||||
// between id and paths
|
file.content = text_editor::Content::with_text(&content);
|
||||||
for file in state.files.iter_mut() {
|
state.current_file = file_index;
|
||||||
match &file.1.path {
|
|
||||||
Some(existing_path) => {
|
|
||||||
if opened_path == existing_path {
|
|
||||||
file.1.content =
|
|
||||||
text_editor::Content::with_text(&content);
|
|
||||||
state.current_file = file.1.id;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let opened_file = File {
|
let opened_file = File {
|
||||||
path: path,
|
path,
|
||||||
content: text_editor::Content::with_text(&content),
|
content: text_editor::Content::with_text(&content),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let opened_file_id = opened_file.id;
|
|
||||||
|
|
||||||
state.files.insert(opened_file.id, opened_file);
|
state.files.push(opened_file);
|
||||||
state.current_file = opened_file_id;
|
state.current_file = state.files.len() - 1;
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileAction::Save => {
|
FileAction::Save => {
|
||||||
let current_file = state.files.get_mut(&state.current_file).expect("problem");
|
let current_file = &mut state.files[state.current_file];
|
||||||
let path = save_file(current_file.path.clone(), current_file.content.text());
|
let path = save_file(current_file.path.clone(), current_file.content.text());
|
||||||
current_file.path = path;
|
current_file.path = path;
|
||||||
}
|
}
|
||||||
FileAction::New => {
|
FileAction::New => {
|
||||||
let default_file = File::default();
|
let default_file = File::default();
|
||||||
let default_file_id = default_file.id;
|
|
||||||
|
|
||||||
state.files.insert(default_file.id, default_file);
|
state.files.push(default_file);
|
||||||
state.current_file = default_file_id;
|
state.current_file = state.files.len() - 1;
|
||||||
|
}
|
||||||
|
FileAction::Close(idx) => {
|
||||||
|
let idx_to_close = match idx {
|
||||||
|
Some(i) => i,
|
||||||
|
None => state.current_file,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.files.remove(idx_to_close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,21 +399,21 @@ fn update(state: &mut State, message: Message) {
|
|||||||
state.selected_view_action = None;
|
state.selected_view_action = None;
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
ViewAction::IncreaseFont => {
|
ViewAction::Increase => {
|
||||||
if state.editor_font_size >= MAX_EDITOR_FONT_SIZE {
|
if state.editor_font_size >= MAX_EDITOR_FONT_SIZE {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.editor_font_size += 2;
|
state.editor_font_size += 2;
|
||||||
}
|
}
|
||||||
ViewAction::DecreaseFont => {
|
ViewAction::Decrease => {
|
||||||
if state.editor_font_size <= MIN_EDITOR_FONT_SIZE {
|
if state.editor_font_size <= MIN_EDITOR_FONT_SIZE {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.editor_font_size -= 2;
|
state.editor_font_size -= 2;
|
||||||
}
|
}
|
||||||
ViewAction::ResetFont => state.editor_font_size = DEFAULT_EDITOR_FONT_SIZE,
|
ViewAction::Reset => state.editor_font_size = DEFAULT_EDITOR_FONT_SIZE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::SwitchTab(file_id) => {
|
Message::SwitchTab(file_id) => {
|
||||||
@@ -430,15 +423,13 @@ fn update(state: &mut State, message: Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn boot() -> State {
|
fn boot() -> State {
|
||||||
let mut files: HashMap<Uuid, File> = HashMap::new();
|
|
||||||
let default_file = File::default();
|
let default_file = File::default();
|
||||||
let default_file_id = default_file.id;
|
|
||||||
|
|
||||||
files.insert(default_file.id, default_file);
|
let files = vec![default_file];
|
||||||
|
|
||||||
State {
|
State {
|
||||||
files: files,
|
files,
|
||||||
current_file: default_file_id,
|
current_file: 0,
|
||||||
editor_font_size: DEFAULT_EDITOR_FONT_SIZE,
|
editor_font_size: DEFAULT_EDITOR_FONT_SIZE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user