diff --git a/CHANGELOG.md b/CHANGELOG.md index df20279..a2e0018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Change Log -======= +============ + +## 0.0.61 + +- Implemented: +- View Menu -> Show End of Line + +## 0.0.60 + +- Implemented: +- View Menu -> Show Space + ## 0.0.59 - Default Tab Fixed diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ece581..ef81e37 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,11 @@ set(PROJECT_UI src/systemfind/systemfinddialog.ui src/systemreplace/systemreplacedialog.ui src/systemsearchresultdialog.ui + src/aboutdialog.ui ) +set(CMAKE_AUTOUIC_SEARCH_PATHS src) + set(PROJECT_SOURCES src/main.cpp src/mainwindow.cpp @@ -100,6 +103,10 @@ set(PROJECT_SOURCES src/systemsearchresultdialog.h src/systemtextdelegate.cpp src/systemtextdelegate.h + src/mainwindow/mainwindowconfigloader.cpp + src/mainwindow/mainwindowconfigloader.h + src/aboutdialog.cpp + src/aboutdialog.h ${PROJECT_UI} ) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user index 8c9f9f7..aec86de 100755 --- a/CMakeLists.txt.user +++ b/CMakeLists.txt.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -102,14 +102,14 @@ 2 false - -DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake + -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} -DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_BUILD_TYPE:STRING=Debug +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} /data/Code/Qt/Notepad-- 0 /data/Code/Qt/Notepad--/build/Desktop-Debug @@ -160,14 +160,14 @@ 2 false - -DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake + -DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} -DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_BUILD_TYPE:STRING=Release +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} /data/Code/Qt/Notepad-- /data/Code/Qt/Notepad--/build/Desktop-Release diff --git a/README.md b/README.md index 5f94178..8163599 100755 --- a/README.md +++ b/README.md @@ -2,9 +2,11 @@ ![gui](assets/lgplv3.png) A simple light text editor. -Notepadqq was my choice editor. Unfortunately it doesn't maintain anymore. I tried to help but there was old PRs and plenty of unanswered issues. The project was a mix of C++/Qt/Javascript/HTML,CSS,Python... -It is a while I didn't code in C++ and my knowledge of Qt is a little. All that said this is a **toy** project, at least for a while. The -- (minus minus) indicates that this is a minimalistic project. I will design GUI just like Notepadqq so there is no new GUI learning. For now I just release for Linux which has Qt6 installed (dynamic compile) and C++ 20. +Notepadqq was my choice editor. Unfortunately it doesn't maintain anymore. I tried to help but there was old PRs and plenty of unanswered issues. The project was a mix of C++/Qt/Javascript/HTML,CSS,Python... . Notepad-- is a pure C++ project. +It is a while I didn't code in C++ and my knowledge of Qt is little. All that said this is a **toy** project, at least for a while. The -- (minus minus) indicates that this is a minimalistic project. I will design GUI just like Notepadqq so there is no new GUI learning. For now I release for Ubuntu-latest, macOS-latest and Windows-latest but you need Qt6 and C++ 20 installed since for now I compile dynamic. + Any contribution is welcome. In a world without walls and fences, who needs **windows** and **gates**? ![gui](assets/gui.png) +![gui](assets/replace-system.png) \ No newline at end of file diff --git a/assets/generate_random_file.sh b/assets/generate_random_file.sh new file mode 100755 index 0000000..d5372f8 --- /dev/null +++ b/assets/generate_random_file.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Function to convert human-readable sizes to bytes +convert_to_bytes() { + local size=$1 + local multiplier=1 + + case "${size: -2}" in + KB|kb) multiplier=1024; size=${size%??} ;; + MB|mb) multiplier=$((1024 * 1024)); size=${size%??} ;; + GB|gb) multiplier=$((1024 * 1024 * 1024)); size=${size%??} ;; + B|b) multiplier=1; size=${size%?} ;; + *) echo "Invalid size format: $size"; exit 1 ;; + esac + + echo $((size * multiplier)) +} + +# Check if file size is provided +if [ -z "$1" ]; then + echo "Usage: $0 (e.g., 2MB, 30KB, 1GB)" + exit 1 +fi + +# Convert human-readable size to bytes +FILE_SIZE_BYTES=$(convert_to_bytes "$1") +OUTPUT_FILE="random_file.txt" + +# Generate random characters +LC_ALL=C tr -dc 'a-zA-Z0-9 \n' "$OUTPUT_FILE" + +echo "Generated random file: $OUTPUT_FILE with size: $1 ($FILE_SIZE_BYTES bytes)" diff --git a/assets/replace-system.png b/assets/replace-system.png new file mode 100644 index 0000000..92e23b2 Binary files /dev/null and b/assets/replace-system.png differ diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp new file mode 100644 index 0000000..3647c5e --- /dev/null +++ b/src/aboutdialog.cpp @@ -0,0 +1,18 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +AboutDialog::AboutDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + + diff --git a/src/aboutdialog.h b/src/aboutdialog.h new file mode 100644 index 0000000..31939b6 --- /dev/null +++ b/src/aboutdialog.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = nullptr); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; +}; + diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui new file mode 100644 index 0000000..c55c7c9 --- /dev/null +++ b/src/aboutdialog.ui @@ -0,0 +1,104 @@ + + + AboutDialog + + + + 0 + 0 + 222 + 198 + + + + Dialog + + + + + 40 + 10 + 171 + 51 + + + + + 24 + + + + Notepad-- + + + + + + 20 + 60 + 111 + 31 + + + + + 12 + + + + Version: 0.1.0 + + + + + + 16 + 90 + 191 + 31 + + + + + 12 + + + + By: Remisa Yousefvand + + + + + + 70 + 160 + 80 + 25 + + + + Close + + + + + + 14 + 123 + 191 + 31 + + + + + 10 + + + + https://github.com/yousefvand + + + + + + diff --git a/src/codeeditor.cpp b/src/codeeditor.cpp index de48379..a21af3e 100755 --- a/src/codeeditor.cpp +++ b/src/codeeditor.cpp @@ -31,6 +31,8 @@ CodeEditor::CodeEditor(QWidget *parent) m_useTabs = Settings::instance()->loadSetting("Indentation", "Option", "Tabs") == "Tabs"; m_indentationWidth = Settings::instance()->loadSetting("Indentation", "Size", "1").toInt(); m_showTabs = Settings::instance()->loadSetting("View", "ShowTabs", "false") == "true"; + m_showSpaces = Settings::instance()->loadSetting("View", "ShowSpaces", "false") == "true"; + m_showEOL = Settings::instance()->loadSetting("View", "ShowEOL", "false") == "true"; m_tabWidth = Settings::instance()->loadSetting("View", "TabWidth", "4").toInt(); } @@ -263,10 +265,29 @@ void CodeEditor::setShowTabs(bool enabled) { } } +void CodeEditor::setShowSpaces(bool enabled) { + if (m_showSpaces != enabled) { + m_showSpaces = enabled; + viewport()->update(); + } +} + +void CodeEditor::setShowEOL(bool enabled) { + if (m_showEOL != enabled) { + m_showEOL = enabled; + viewport()->update(); + } +} + bool CodeEditor::showTabs() const { return m_showTabs; } +bool CodeEditor::showSpaces() const { + return m_showSpaces; +} + +// TODO: Implement in UI void CodeEditor::setTabWidth(int width = 4) { m_tabWidth = width; viewport()->update(); // Trigger a repaint to apply the new width @@ -275,8 +296,6 @@ void CodeEditor::setTabWidth(int width = 4) { void CodeEditor::paintEvent(QPaintEvent *event) { QPlainTextEdit::paintEvent(event); - if (!m_showTabs) return; - QPainter painter(viewport()); painter.setPen(Qt::gray); @@ -288,9 +307,9 @@ void CodeEditor::paintEvent(QPaintEvent *event) { int blockStart = block.position(); QTextCursor blockCursor(block); - // Iterate over characters in the block + // Handle Tabs and Spaces in the line for (int i = 0; i < text.length(); ++i) { - if (text[i] == '\t') { + if (text[i] == '\t' && m_showTabs) { // Move the cursor to the tab character blockCursor.setPosition(blockStart + i); @@ -298,13 +317,40 @@ void CodeEditor::paintEvent(QPaintEvent *event) { QRect rect = cursorRect(blockCursor); // Adjust the position for the tab symbol - //QPoint position(rect.left(), rect.top() + metrics.ascent()); QPoint position(rect.left() + metrics.ascent(), rect.top() + metrics.ascent()); // Draw the tab symbol painter.drawText(position, "→"); + } else if (text[i] == ' ' && m_showSpaces) { + // Move the cursor to the space character + blockCursor.setPosition(blockStart + i); + + // Get the rectangle of the cursor position + QRect rect = cursorRect(blockCursor); + + // Adjust the position for the space dot + QPoint position( + rect.left() + metrics.horizontalAdvance(' ') / 4, // Push right slightly + rect.top() + metrics.ascent() / 2 + metrics.height() / 3 // Vertically center the dot lower + ); + + // Draw the space as a dot + painter.drawText(position, "."); } } + + // Add the EOL character if enabled + if (m_showEOL) { + blockCursor.setPosition(blockStart + text.length()); // Move to the end of the line + QRect rect = cursorRect(blockCursor); + + // Position for the EOL character + QPoint position(rect.left() + metrics.horizontalAdvance(' '), rect.top() + metrics.ascent()); + + // Draw the EOL character (e.g., "↵") + painter.drawText(position, "↵"); + } + block = block.next(); } } diff --git a/src/codeeditor.h b/src/codeeditor.h index a8a5360..3023f7f 100755 --- a/src/codeeditor.h +++ b/src/codeeditor.h @@ -25,7 +25,10 @@ class CodeEditor : public QPlainTextEdit { void goToLineInText(int lineNumber); void gotoLineInEditor(int lineNumber); void setShowTabs(bool enabled); + void setShowSpaces(bool enabled); + void setShowEOL(bool enabled); bool showTabs() const; + bool showSpaces() const; void setTabWidth(int width); protected: @@ -46,6 +49,8 @@ private slots: bool m_useTabs; int m_indentationWidth; bool m_showTabs = false; + bool m_showSpaces = false; + bool m_showEOL = false; int m_tabWidth; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6e9ae35..41031fa 100755 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -9,6 +9,7 @@ #include "mainwindow.h" #include "codeeditor.h" #include "settings.h" +#include "mainwindow/mainwindowconfigloader.h" #include "mainwindow/textoperations.h" #include "mainwindow/recentfiles.h" #include "mainwindow/session.h" @@ -19,6 +20,7 @@ #include "replace/replacedialog.h" #include "systemfind/systemfinddialog.h" #include "systemreplace/systemreplacedialog.h" +#include "aboutdialog.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), @@ -56,6 +58,9 @@ MainWindow::MainWindow(QWidget* parent) fileOperations->newDocument(); Helpers::zMenu(ui->menu_Language, this); + m_mainWindowConfigLoader = new MainWindowConfigLoader(this); + m_mainWindowConfigLoader->loadMainWindowConfig(); + qDebug() << "MainWindow initialized..."; } @@ -69,6 +74,7 @@ MainWindow::~MainWindow() { delete textOperations; delete m_systemFindDialog; delete m_systemReplaceDialog; + delete m_mainWindowConfigLoader; } Ui::MainWindow* MainWindow::getUi() const { @@ -496,8 +502,7 @@ void MainWindow::on_action_Find_triggered() { void MainWindow::on_action_Show_Tabs_triggered(bool checked) { - qDebug() << "Show Tabs is: " << checked; - Settings::instance()->saveSetting("View", "ShowTabs", checked ? "true" : "false"); + Settings::instance()->saveSetting("View", "ShowTabs", checked); for (int i = 0; i < ui->documentsTab->count(); ++i) { Document *doc = qobject_cast(ui->documentsTab->widget(i)); @@ -507,6 +512,44 @@ void MainWindow::on_action_Show_Tabs_triggered(bool checked) } } +void MainWindow::on_actionShow_Spaces_triggered(bool checked) +{ + Settings::instance()->saveSetting("View", "ShowSpaces", checked); + + for (int i = 0; i < ui->documentsTab->count(); ++i) { + Document *doc = qobject_cast(ui->documentsTab->widget(i)); + if (doc) { + doc->editor()->setShowSpaces(checked); + } + } +} + +void MainWindow::on_actionShow_End_of_Lines_triggered(bool checked) +{ + Settings::instance()->saveSetting("View", "ShowEOL", checked); + + for (int i = 0; i < ui->documentsTab->count(); ++i) { + Document *doc = qobject_cast(ui->documentsTab->widget(i)); + if (doc) { + doc->editor()->setShowEOL(checked); + } + } +} + + + + + + + + + + + + + + + @@ -651,3 +694,18 @@ void MainWindow::setActiveDocumentEditorInReplaceDialog() { +void MainWindow::on_action_About_Notepad_triggered() +{ + AboutDialog dialog(this); + dialog.exec(); +} + + +void MainWindow::on_actionAbout_Qt_triggered() +{ + QMessageBox::aboutQt(this, tr("About Qt")); +} + + + + diff --git a/src/mainwindow.h b/src/mainwindow.h index be9e7f0..7d8f2cf 100755 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -24,6 +24,7 @@ class Session; class Settings; class RecentFiles; class FileOperations; +class MainWindowConfigLoader; class MainWindow : public QMainWindow { Q_OBJECT @@ -147,6 +148,14 @@ private slots: void on_action_Show_Tabs_triggered(bool checked); + void on_actionShow_Spaces_triggered(bool checked); + + void on_action_About_Notepad_triggered(); + + void on_actionAbout_Qt_triggered(); + + void on_actionShow_End_of_Lines_triggered(bool checked); + private: Ui::MainWindow* ui; FileOperations* fileOperations; @@ -176,4 +185,5 @@ private slots: ReplaceDialog* replaceDialog; SearchOptions* m_searchOptions; Find* m_find = nullptr; + MainWindowConfigLoader* m_mainWindowConfigLoader; }; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index c91e5b8..f35d479 100755 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -189,7 +189,7 @@ - + @@ -1913,7 +1913,7 @@ S&how Spaces - + true diff --git a/src/mainwindow/mainwindowconfigloader.cpp b/src/mainwindow/mainwindowconfigloader.cpp new file mode 100644 index 0000000..40c8c4c --- /dev/null +++ b/src/mainwindow/mainwindowconfigloader.cpp @@ -0,0 +1,29 @@ +#include "../settings.h" +#include "../mainwindow.h" +#include "../ui_mainwindow.h" +#include "src/ui_mainwindow.h" +#include "mainwindowconfigloader.h" + +MainWindowConfigLoader::MainWindowConfigLoader(MainWindow *mainWindow) : m_mainWindow(mainWindow) {} + +void MainWindowConfigLoader::loadMainWindowConfig() { + if (m_mainWindow && m_mainWindow->getUi()) { + m_mainWindow->getUi()->action_Show_Tabs->setChecked(showTabs()); + m_mainWindow->getUi()->actionShow_Spaces->setChecked(showSpaces()); + m_mainWindow->getUi()->actionShow_End_of_Lines->setChecked(showEOL()); + } +} + +bool MainWindowConfigLoader::showTabs() const { + return Settings::instance()->loadSetting("View", "ShowTabs", "false") == true; +} + +bool MainWindowConfigLoader::showSpaces() const { + return Settings::instance()->loadSetting("View", "ShowSpaces", "false") == true; +} + +bool MainWindowConfigLoader::showEOL() const { + return Settings::instance()->loadSetting("View", "ShowEOL", "false") == true; +} + + diff --git a/src/mainwindow/mainwindowconfigloader.h b/src/mainwindow/mainwindowconfigloader.h new file mode 100644 index 0000000..d69a9dc --- /dev/null +++ b/src/mainwindow/mainwindowconfigloader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../mainwindow.h" + +class MainWindowConfigLoader +{ +public: + explicit MainWindowConfigLoader(MainWindow *mainWindow); + void loadMainWindowConfig(); + +private: + MainWindow* m_mainWindow; + + bool showTabs() const; + bool showSpaces() const; + bool showEOL() const; +}; +