Start implementing rich-text editor
This commit is contained in:
committed by
James Graham
parent
1f723d1fdf
commit
9cbe9f7280
249
src/libneochat/nestedlisthelper.cpp
Normal file
249
src/libneochat/nestedlisthelper.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Nested list helper
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "nestedlisthelper_p.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
#include <QTextList>
|
||||
|
||||
NestedListHelper::NestedListHelper()
|
||||
{
|
||||
listBottomMargin = 12;
|
||||
listTopMargin = 12;
|
||||
listNoMargin = 0;
|
||||
}
|
||||
|
||||
bool NestedListHelper::handleBeforeKeyPressEvent(QKeyEvent *event, const QTextCursor &cursor)
|
||||
{
|
||||
// Only attempt to handle Backspace while on a list
|
||||
if ((event->key() != Qt::Key_Backspace) || (!cursor.currentList())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
if (!cursor.hasSelection() && cursor.currentList() && event->key() == Qt::Key_Backspace && cursor.atBlockStart()) {
|
||||
handleOnIndentLess(cursor);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
bool NestedListHelper::canIndent(const QTextCursor &textCursor) const
|
||||
{
|
||||
if ((textCursor.block().isValid())
|
||||
// && ( textEdit->textCursor().block().previous().isValid() )
|
||||
) {
|
||||
const QTextBlock block = textCursor.block();
|
||||
const QTextBlock prevBlock = textCursor.block().previous();
|
||||
if (block.textList()) {
|
||||
if (prevBlock.textList()) {
|
||||
return block.textList()->format().indent() <= prevBlock.textList()->format().indent();
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NestedListHelper::canDedent(const QTextCursor &textCursor) const
|
||||
{
|
||||
QTextBlock thisBlock = textCursor.block();
|
||||
QTextBlock nextBlock = thisBlock.next();
|
||||
if (thisBlock.isValid()) {
|
||||
int nextBlockIndent = 0;
|
||||
if (nextBlock.isValid() && nextBlock.textList()) {
|
||||
nextBlockIndent = nextBlock.textList()->format().indent();
|
||||
}
|
||||
if (thisBlock.textList()) {
|
||||
const int thisBlockIndent = thisBlock.textList()->format().indent();
|
||||
if (thisBlockIndent >= nextBlockIndent) {
|
||||
return thisBlockIndent > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NestedListHelper::handleAfterKeyPressEvent(QKeyEvent *event, const QTextCursor &cursor)
|
||||
{
|
||||
// Only attempt to handle Backspace and Return
|
||||
if ((event->key() != Qt::Key_Backspace) && (event->key() != Qt::Key_Return)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
if (!cursor.hasSelection() && cursor.currentList()) {
|
||||
// Check if we're on the last list item.
|
||||
// itemNumber is zero indexed
|
||||
QTextBlock currentBlock = cursor.block();
|
||||
if (cursor.currentList()->count() == cursor.currentList()->itemNumber(currentBlock) + 1) {
|
||||
// Last block in this list, but may have just gained another list below.
|
||||
if (currentBlock.next().textList()) {
|
||||
reformatList(cursor.block());
|
||||
}
|
||||
reformatList(cursor.block());
|
||||
|
||||
// No need to reformatList in this case. reformatList is slow.
|
||||
if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Backspace)) {
|
||||
handled = true;
|
||||
}
|
||||
} else {
|
||||
reformatList(cursor.block());
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
void NestedListHelper::processList(QTextList *list)
|
||||
{
|
||||
QTextBlock block = list->item(0);
|
||||
const int thisListIndent = list->format().indent();
|
||||
|
||||
QTextCursor cursor = QTextCursor(block);
|
||||
list = cursor.createList(list->format());
|
||||
bool processingSubList = false;
|
||||
while (block.next().textList() != nullptr) {
|
||||
block = block.next();
|
||||
|
||||
QTextList *nextList = block.textList();
|
||||
const int nextItemIndent = nextList->format().indent();
|
||||
if (nextItemIndent < thisListIndent) {
|
||||
return;
|
||||
} else if (nextItemIndent > thisListIndent) {
|
||||
if (processingSubList) {
|
||||
continue;
|
||||
}
|
||||
processingSubList = true;
|
||||
processList(nextList);
|
||||
} else {
|
||||
processingSubList = false;
|
||||
list->add(block);
|
||||
}
|
||||
}
|
||||
// delete nextList;
|
||||
// nextList = 0;
|
||||
}
|
||||
|
||||
void NestedListHelper::reformatList(QTextBlock block)
|
||||
{
|
||||
if (block.textList()) {
|
||||
const int minimumIndent = block.textList()->format().indent();
|
||||
|
||||
// Start at the top of the list
|
||||
while (block.previous().textList() != nullptr) {
|
||||
if (block.previous().textList()->format().indent() < minimumIndent) {
|
||||
break;
|
||||
}
|
||||
block = block.previous();
|
||||
}
|
||||
|
||||
processList(block.textList());
|
||||
}
|
||||
}
|
||||
|
||||
QTextCursor NestedListHelper::topOfSelection(QTextCursor cursor)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
cursor.setPosition(qMin(cursor.position(), cursor.anchor()));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
QTextCursor NestedListHelper::bottomOfSelection(QTextCursor cursor)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
cursor.setPosition(qMax(cursor.position(), cursor.anchor()));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void NestedListHelper::handleOnIndentMore(const QTextCursor &textCursor)
|
||||
{
|
||||
QTextCursor cursor = textCursor;
|
||||
|
||||
QTextListFormat listFmt;
|
||||
if (!cursor.currentList()) {
|
||||
QTextListFormat::Style style;
|
||||
cursor = topOfSelection(textCursor);
|
||||
cursor.movePosition(QTextCursor::PreviousBlock);
|
||||
if (cursor.currentList()) {
|
||||
style = cursor.currentList()->format().style();
|
||||
} else {
|
||||
cursor = bottomOfSelection(textCursor);
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
|
||||
if (cursor.currentList()) {
|
||||
style = cursor.currentList()->format().style();
|
||||
} else {
|
||||
style = QTextListFormat::ListDisc;
|
||||
}
|
||||
}
|
||||
handleOnBulletType(style, textCursor);
|
||||
} else {
|
||||
listFmt = cursor.currentList()->format();
|
||||
listFmt.setIndent(listFmt.indent() + 1);
|
||||
|
||||
cursor.createList(listFmt);
|
||||
reformatList(textCursor.block());
|
||||
}
|
||||
}
|
||||
|
||||
void NestedListHelper::handleOnIndentLess(const QTextCursor &textCursor)
|
||||
{
|
||||
QTextCursor cursor = textCursor;
|
||||
QTextList *currentList = cursor.currentList();
|
||||
if (!currentList) {
|
||||
return;
|
||||
}
|
||||
QTextListFormat listFmt = currentList->format();
|
||||
if (listFmt.indent() > 1) {
|
||||
listFmt.setIndent(listFmt.indent() - 1);
|
||||
cursor.createList(listFmt);
|
||||
reformatList(cursor.block());
|
||||
} else {
|
||||
QTextBlockFormat bfmt;
|
||||
bfmt.setObjectIndex(-1);
|
||||
cursor.setBlockFormat(bfmt);
|
||||
reformatList(cursor.block().next());
|
||||
}
|
||||
}
|
||||
|
||||
void NestedListHelper::handleOnBulletType(int styleIndex, const QTextCursor &textCursor)
|
||||
{
|
||||
QTextCursor cursor = textCursor;
|
||||
if (styleIndex != 0) {
|
||||
auto style = static_cast<QTextListFormat::Style>(styleIndex);
|
||||
QTextList *currentList = cursor.currentList();
|
||||
QTextListFormat listFmt;
|
||||
|
||||
cursor.beginEditBlock();
|
||||
|
||||
if (currentList) {
|
||||
listFmt = currentList->format();
|
||||
listFmt.setStyle(style);
|
||||
currentList->setFormat(listFmt);
|
||||
} else {
|
||||
listFmt.setStyle(style);
|
||||
cursor.createList(listFmt);
|
||||
}
|
||||
|
||||
cursor.endEditBlock();
|
||||
} else {
|
||||
QTextBlockFormat bfmt;
|
||||
bfmt.setObjectIndex(-1);
|
||||
cursor.setBlockFormat(bfmt);
|
||||
}
|
||||
|
||||
reformatList(textCursor.block());
|
||||
}
|
||||
Reference in New Issue
Block a user