Init project

pull/4/head
reionwong 5 years ago
parent df738e2b38
commit 1d34ab9b95

2
.gitignore vendored

@ -50,3 +50,5 @@ compile_commands.json
# QtCreator local machine specific files for imported projects
*creator.user*
build/*

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.14)
project(cutefish-texteditor LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Core Quick REQUIRED)
find_package(KF5SyntaxHighlighting)
set(PROJECT_SOURCES
src/main.cpp
src/documenthandler.cpp
qml.qrc
)
add_executable(cutefish-texteditor
${PROJECT_SOURCES}
)
target_link_libraries(cutefish-texteditor
PRIVATE
Qt5::Core
Qt5::Quick
KF5::SyntaxHighlighting
)

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/TextEditor.qml</file>
</qresource>
</RCC>

@ -0,0 +1,179 @@
import QtQuick 2.15
import QtQml 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import FishUI 1.0 as FishUI
import Cutefish.TextEditor 1.0
Item {
id: control
property alias fileUrl: document.fileUrl
property bool showLineNumbers: true
DocumentHandler {
id: document
document: body.textDocument
cursorPosition: body.cursorPosition
selectionStart: body.selectionStart
selectionEnd: body.selectionEnd
backgroundColor: FishUI.Theme.backgroundColor
enableSyntaxHighlighting: true
onSearchFound: {
body.select(start, end)
}
}
ScrollView {
id: _scrollView
anchors.fill: parent
Keys.enabled: true
Keys.forwardTo: body
Flickable {
id: _flickable
boundsBehavior: Flickable.StopAtBounds
boundsMovement: Flickable.StopAtBounds
TextArea.flickable: TextArea {
id: body
text: document.text
selectByKeyboard: true
selectByMouse: true
persistentSelection: true
textFormat: TextEdit.PlainText
wrapMode: TextEdit.WrapAnywhere
activeFocusOnPress: true
activeFocusOnTab: true
leftPadding: _linesCounter.width + FishUI.Units.smallSpacing
padding: FishUI.Units.smallSpacing
color: FishUI.Theme.textColor
font.family: "Noto Mono"
background: Rectangle {
color: FishUI.Theme.backgroundColor
}
Loader {
id: _linesCounter
asynchronous: true
active: control.showLineNumbers && !document.isRich
anchors.left: parent.left
anchors.top: parent.top
height: Math.max(_flickable.contentHeight, control.height)
width: active ? 32 : 0
sourceComponent: _linesCounterComponent
}
}
}
}
Component {
id: _linesCounterComponent
ListView {
id: _linesCounterList
anchors.fill: parent
anchors.topMargin: body.topPadding + body.textMargin
model: document.lineCount
Binding on currentIndex {
value: document.currentLineIndex
restoreMode: Binding.RestoreBindingOrValue
}
Timer {
id: _lineIndexTimer
interval: 250
onTriggered: _linesCounterList.currentIndex = document.currentLineIndex
}
Connections {
target: document
function onLineCountChanged() {
_lineIndexTimer.restart()
}
}
orientation: ListView.Vertical
interactive: false
snapMode: ListView.NoSnap
boundsBehavior: Flickable.StopAtBounds
boundsMovement :Flickable.StopAtBounds
preferredHighlightBegin: 0
preferredHighlightEnd: width
highlightRangeMode: ListView.StrictlyEnforceRange
highlightMoveDuration: 0
highlightFollowsCurrentItem: false
highlightResizeDuration: 0
highlightMoveVelocity: -1
highlightResizeVelocity: -1
maximumFlickVelocity: 0
delegate: Row {
id: _delegate
readonly property int line : index
property bool foldable : control.document.isFoldable(line)
width: ListView.view.width
height: Math.max(fontSize, document.lineHeight(line))
readonly property real fontSize: control.body.font.pointSize
readonly property bool isCurrentItem: ListView.isCurrentItem
// Connections {
// target: control.body
// function onContentHeightChanged() {
// if (_delegate.isCurrentItem) {
// console.log("Updating line height")
// _delegate.height = control.document.lineHeight(_delegate.line)
// _delegate.foldable = control.document.isFoldable(_delegate.line)
// }
// }
// }
Label {
width: 32
height: parent.height
opacity: isCurrentItem ? 1 : 0.7
color: isCurrentItem ? FishUI.Theme.highlightColor
: FishUI.Theme.textColor
font.pointSize: body.font.pointSize
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "Monospace"
text: index + 1
}
}
}
}
function forceActiveFocus() {
body.forceActiveFocus()
}
function goToLine(line) {
if (line > 0 && line <= body.lineCount) {
body.cursorPosition = document.goToLine(line - 1)
body.forceActiveFocus()
}
}
}

@ -0,0 +1,15 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import FishUI 1.0 as FishUI
FishUI.Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
TextEditor {
anchors.fill: parent
fileUrl: "file:///home/reion/Cutefish/core/notificationd/view.cpp"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,772 @@
/****************************************************************************
* *
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef DOCUMENTHANDLER_H
#define DOCUMENTHANDLER_H
#include <QAbstractListModel>
#include <QDebug>
#include <QFont>
#include <QObject>
#include <QTextCursor>
#include <QThread>
#include <QTimer>
#include <QUrl>
QT_BEGIN_NAMESPACE
class QFileSystemWatcher;
class QTextDocument;
class QQuickTextDocument;
QT_END_NAMESPACE
namespace KSyntaxHighlighting
{
class Repository;
class SyntaxHighlighter;
}
/**
* @brief The AlertAction struct
*/
struct AlertAction {
QString label;
std::function<void()> action;
};
/**
* @brief The DocumentAlert class
*/
class DocumentAlert : public QObject
{
Q_OBJECT
Q_PROPERTY(QString title MEMBER m_title CONSTANT FINAL)
Q_PROPERTY(QString body MEMBER m_body CONSTANT FINAL)
Q_PROPERTY(uint level MEMBER m_level CONSTANT FINAL)
Q_PROPERTY(QStringList actionLabels READ actionLabels CONSTANT FINAL)
public:
enum LEVEL : uint { INFO_LEVEL = 0, WARNING_LEVEL = 1, DANGER_LEVEL = 2 };
DocumentAlert(const QString &title, const QString &body, const uint &level, const int &id, QObject *parent = nullptr)
: QObject(parent)
{
this->m_title = title;
this->m_body = body;
this->m_level = level;
this->m_id = id;
}
/**
* @brief setIndex
* @param index
*/
void setIndex(const int &index)
{
this->m_index = index;
}
/**
* @brief setActions
* @param actions
*/
void setActions(QVector<AlertAction> actions)
{
this->m_actions = actions;
}
/**
* @brief getId
* @return
*/
int getId() const
{
return this->m_id;
}
/**
* @brief operator ==
* @param other
* @param other2
* @return
*/
friend bool operator==(const DocumentAlert &other, const DocumentAlert &other2)
{
return other.getId() == other2.getId();
}
/**
* @brief actionLabels
* @return
*/
QStringList actionLabels() const
{
return std::accumulate(this->m_actions.constBegin(), this->m_actions.constEnd(), QStringList(), [](QStringList &labels, const AlertAction &action) -> QStringList {
labels << action.label;
return labels;
});
}
private:
QString m_title;
QString m_body;
uint m_level;
int m_index = -1;
int m_id = -1;
QVector<AlertAction> m_actions;
public slots:
/**
* @brief triggerAction
* @param actionIndex
* @param alertIndex
*/
void triggerAction(const int &actionIndex, const int &alertIndex)
{
qDebug() << "TRIGGERING DOCUMENT ACTION AT INDEX << " << actionIndex << alertIndex;
this->m_actions.at(actionIndex).action();
emit this->done(alertIndex);
}
signals:
/**
* @brief done
* @param index
*/
void done(int index);
};
/**
* @brief The Alerts class
*/
class Alerts : public QAbstractListModel
{
Q_OBJECT
public:
enum ROLES : uint { ALERT = Qt::DisplayRole + 1 };
enum ALERT_TYPES : uint { MISSING, UNSAVED, MODIFIED, SAVE_ERROR };
explicit Alerts(QObject *parent = nullptr);
~Alerts();
QVariant data(const QModelIndex &index, int role) const override final;
int rowCount(const QModelIndex &parent = QModelIndex()) const override final;
QHash<int, QByteArray> roleNames() const override;
void append(DocumentAlert *alert);
private:
QVector<DocumentAlert *> m_alerts;
bool contains(DocumentAlert *const alert);
};
/**
* @brief The FileLoader class
*/
class FileLoader : public QObject
{
Q_OBJECT
public slots:
/**
* @brief loadFile
* @param url
*/
void loadFile(const QUrl &url);
signals:
/**
* @brief fileReady
* @param array
* @param url
*/
void fileReady(QString array, QUrl url);
};
/**
* @brief The DocumentHandler class
*/
class DocumentHandler : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily NOTIFY fontFamilyChanged)
Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
Q_PROPERTY(bool uppercase READ uppercase WRITE setUppercase NOTIFY uppercaseChanged)
Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged)
Q_PROPERTY(bool isRich READ getIsRich NOTIFY isRichChanged)
Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
Q_PROPERTY(qreal tabSpace READ tabSpace WRITE setTabSpace NOTIFY tabSpaceChanged)
Q_PROPERTY(QString fileName READ fileName NOTIFY fileUrlChanged)
Q_PROPERTY(QString fileType READ fileType NOTIFY fileUrlChanged)
Q_PROPERTY(QVariantMap fileInfo READ fileInfo NOTIFY fileInfoChanged)
Q_PROPERTY(QUrl fileUrl READ fileUrl WRITE setFileUrl NOTIFY fileUrlChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(bool externallyModified READ getExternallyModified WRITE setExternallyModified NOTIFY externallyModifiedChanged)
Q_PROPERTY(bool modified READ getModified NOTIFY modifiedChanged)
Q_PROPERTY(bool autoReload READ getAutoReload WRITE setAutoReload NOTIFY autoReloadChanged)
Q_PROPERTY(bool autoSave READ autoSave WRITE setAutoSave NOTIFY autoSaveChanged)
Q_PROPERTY(QString formatName READ formatName WRITE setFormatName NOTIFY formatNameChanged)
Q_PROPERTY(int currentLineIndex READ getCurrentLineIndex NOTIFY currentLineIndexChanged)
Q_PROPERTY(Alerts *alerts READ getAlerts CONSTANT FINAL)
Q_PROPERTY(QColor backgroundColor READ getBackgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged)
Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged)
Q_PROPERTY(bool enableSyntaxHighlighting READ enableSyntaxHighlighting WRITE setEnableSyntaxHighlighting NOTIFY enableSyntaxHighlightingChanged)
Q_PROPERTY(bool findWholeWords MEMBER m_findWholeWords NOTIFY findWholeWordsChanged)
Q_PROPERTY(bool findCaseSensitively MEMBER m_findCaseSensitively NOTIFY findCaseSensitivelyChanged)
public:
explicit DocumentHandler(QObject *parent = nullptr);
~DocumentHandler();
/**
* @brief document
* @return
*/
QQuickTextDocument *document() const;
/**
* @brief setDocument
* @param document
*/
void setDocument(QQuickTextDocument *document);
/**
* @brief cursorPosition
* @return
*/
int cursorPosition() const;
/**
* @brief setCursorPosition
* @param position
*/
void setCursorPosition(int position);
/**
* @brief selectionStart
* @return
*/
int selectionStart() const;
/**
* @brief setSelectionStart
* @param position
*/
void setSelectionStart(int position);
/**
* @brief selectionEnd
* @return
*/
int selectionEnd() const;
/**
* @brief setSelectionEnd
* @param position
*/
void setSelectionEnd(int position);
/**
* @brief fontFamily
* @return
*/
QString fontFamily() const;
/**
* @brief setFontFamily
* @param family
*/
void setFontFamily(const QString &family);
/**
* @brief textColor
* @return
*/
QColor textColor() const;
/**
* @brief setTextColor
* @param color
*/
void setTextColor(const QColor &color);
/**
* @brief alignment
* @return
*/
Qt::Alignment alignment() const;
/**
* @brief setAlignment
* @param alignment
*/
void setAlignment(Qt::Alignment alignment);
/**
* @brief bold
* @return
*/
bool bold() const;
/**
* @brief setBold
* @param bold
*/
void setBold(bool bold);
/**
* @brief uppercase
* @return
*/
bool uppercase() const;
/**
* @brief setUppercase
* @param uppercase
*/
void setUppercase(bool uppercase);
/**
* @brief italic
* @return
*/
bool italic() const;
/**
* @brief setItalic
* @param italic
*/
void setItalic(bool italic);
/**
* @brief underline
* @return
*/
bool underline() const;
/**
* @brief setUnderline
* @param underline
*/
void setUnderline(bool underline);
/**
* @brief getIsRich
* @return
*/
bool getIsRich() const;
/**
* @brief fontSize
* @return
*/
int fontSize() const;
/**
* @brief setFontSize
* @param size
*/
void setFontSize(int size);
qreal tabSpace() const;
void setTabSpace(qreal value);
/**
* @brief fileName
* @return
*/
QString fileName() const;
/**
* @brief fileType
* @return
*/
QString fileType() const;
/**
* @brief fileUrl
* @return
*/
QUrl fileUrl() const;
/**
* @brief setFileUrl
* @param url
*/
void setFileUrl(const QUrl &url);
/**
* @brief fileInfo
* @return
*/
QVariantMap fileInfo() const;
/**
* @brief text
* @return
*/
inline QString text() const
{
return m_text;
}
/**
* @brief setText
* @param text
*/
void setText(const QString &text);
/**
* @brief getAutoReload
* @return
*/
bool getAutoReload() const;
/**
* @brief setAutoReload
* @param value
*/
void setAutoReload(const bool &value);
/**
* @brief autoSave
* @return
*/
bool autoSave() const;
/**
* @brief setAutoSave
* @param value
*/
void setAutoSave(const bool &value);
/**
* @brief getModified
* @return
*/
bool getModified() const;
/**
* @brief getExternallyModified
* @return
*/
bool getExternallyModified() const;
/**
* @brief setExternallyModified
* @param value
*/
void setExternallyModified(const bool &value);
/**
* @brief formatName
* @return
*/
QString formatName() const;
/**
* @brief setFormatName
* @param formatName
*/
void setFormatName(const QString &formatName);
/**
* @brief getBackgroundColor
* @return
*/
QColor getBackgroundColor() const;
/**
* @brief setBackgroundColor
* @param color
*/
void setBackgroundColor(const QColor &color);
/**
* @brief getAlerts
* @return
*/
Alerts *getAlerts() const;
/**
* @brief theme
* @return
*/
QString theme() const;
/**
* @brief setTheme
* @param theme
*/
void setTheme(const QString &theme);
/**
* @brief enableSyntaxHighlighting
* @return
*/
bool enableSyntaxHighlighting() const;
/**
* @brief setEnableSyntaxHighlighting
* @param value
*/
void setEnableSyntaxHighlighting(const bool &value);
/**
* @brief isDark
* @param color
* @return
*/
static bool isDark(const QColor &color)
{
const double darkness = 1 - (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255;
return (darkness > 0.5);
}
public slots:
/**
* @brief load
* @param url
*/
void load(const QUrl &url);
/**
* @brief saveAs
* @param url
*/
void saveAs(const QUrl &url);
/**
* @brief find
* @param query
*/
void find(const QString &query, const bool &forward = true);
void replace(const QString &query, const QString &value);
void replaceAll(const QString &query, const QString &value);
bool isFoldable(const int &line) const;
bool isFolded(const int &line) const;
void toggleFold(const int &line);
/**
* @brief lineHeight
* @param line
* @return
*/
int lineHeight(const int &line);
int lineCount();
/**
* @brief getCurrentLineIndex
* @return
*/
int getCurrentLineIndex();
int goToLine(const int &line);
/**
* @brief getLanguageNameList
* @return
*/
static const QStringList getLanguageNameList();
/**
* @brief getLanguageNameFromFileName
* @param fileName
* @return
*/
static const QString getLanguageNameFromFileName(const QUrl &fileName);
/**
* @brief getThemes
* @return
*/
static const QStringList getThemes();
signals:
void documentChanged();
void fileSaved();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void fontFamilyChanged();
void textColorChanged();
void alignmentChanged();
void boldChanged();
void uppercaseChanged();
void italicChanged();
void underlineChanged();
void isRichChanged();
void lineCountChanged();
void fontSizeChanged();
void tabSpaceChanged();
void textChanged();
void fileUrlChanged();
void fileInfoChanged();
void loaded(const QUrl &url);
void error(const QString &message);
void loadFile(QUrl url);
void autoReloadChanged();
void autoSaveChanged();
void externallyModifiedChanged();
void backgroundColorChanged();
void formatNameChanged();
void modifiedChanged();
void currentLineIndexChanged();
void enableSyntaxHighlightingChanged();
void themeChanged();
void searchFound(int start, int end);
void findCaseSensitivelyChanged();
void findWholeWordsChanged();
// void cursorYPosChanged();
private:
void reset();
void setStyle();
QTextCursor textCursor() const;
QTextDocument *textDocument() const;
void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
QQuickTextDocument *m_document;
QFileSystemWatcher *m_watcher;
int m_cursorPosition;
int m_selectionStart;
int m_selectionEnd;
bool isRich = false;
QFont m_font;
int m_fontSize = 12;
qreal m_tabSpace = 8;
QUrl m_fileUrl;
QThread m_worker;
QString m_text;
bool m_autoReload = false;
bool m_autoSave = false;
bool m_externallyModified = false;
bool m_internallyModified = false;
bool m_findCaseSensitively = false;
bool m_findWholeWords = false;
QColor m_backgroundColor;
static int m_instanceCount;
QString m_formatName = "None";
static KSyntaxHighlighting::Repository *m_repository;
KSyntaxHighlighting::SyntaxHighlighter *m_highlighter;
bool m_enableSyntaxHighlighting = false;
QString m_theme;
QString m_searchQuery;
QString m_replaceText;
Alerts *m_alerts;
DocumentAlert *missingAlert();
DocumentAlert *externallyModifiedAlert();
DocumentAlert *canNotSaveAlert(const QString &details);
QTimer m_autoSaveTimer;
void refreshAllBlocks();
};
#endif // DOCUMENTHANDLER_H

@ -0,0 +1,25 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "documenthandler.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
qmlRegisterType<DocumentHandler>("Cutefish.TextEditor", 1, 0, "DocumentHandler");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Loading…
Cancel
Save