[Spreadsheet] Refactor keyboard handling

LineEdit no longer actually handles motion, it simply indicates which
action was taken to cause it to lose focus (e.g. which key was pressed).
It's up to the client code to determine what this means. This allows
significant consolidation of keyboard-handling logic, and the
implementation of more extensive keyboard navigation features.

New keyboard shortcuts include a tab counter to implement auto-return,
plus Ctrl->Arrow, End, Home, Ctrl-End, and Ctrl-Home, matching the
behavior of OpenOffice, LibreOffice, etc.

Block selection via keyboard has also been added by holding down the
shift key during navigation with the arrow keys (this also works in
combination with the Ctrl modifier for region navigation).
This commit is contained in:
Chris Hennes
2021-10-06 23:37:20 -05:00
committed by Chris Hennes
parent c2c5ae1bf0
commit 403a569315
12 changed files with 394 additions and 182 deletions

View File

@@ -982,6 +982,14 @@ void PropertySheet::getSpans(CellAddress address, int & rows, int & cols) const
}
}
App::CellAddress Spreadsheet::PropertySheet::getAnchor(App::CellAddress address) const
{
if (auto anchor = mergedCells.find(address); anchor != mergedCells.end())
return anchor->second;
else
return address;
}
bool PropertySheet::isMergedCell(CellAddress address) const
{
return mergedCells.find(address) != mergedCells.end();

View File

@@ -148,6 +148,8 @@ public:
void getSpans(App::CellAddress address, int &rows, int &cols) const;
App::CellAddress getAnchor(App::CellAddress address) const;
bool isMergedCell(App::CellAddress address) const;
bool isHidden(App::CellAddress address) const;

View File

@@ -1063,6 +1063,11 @@ bool Sheet::isMergedCell(CellAddress address) const
return cells.isMergedCell(address);
}
App::CellAddress Spreadsheet::Sheet::getAnchor(App::CellAddress address) const
{
return cells.getAnchor(address);
}
/**
* @brief Set column with of column \a col to \a width-
* @param col Index of column.

View File

@@ -110,6 +110,8 @@ public:
bool isMergedCell(App::CellAddress address) const;
App::CellAddress getAnchor(App::CellAddress address) const;
void setColumnWidth(int col, int width);
int getColumnWidth(int col) const;

View File

@@ -26,68 +26,58 @@
# include <QKeyEvent>
#endif
#include <QCoreApplication>
#include "LineEdit.h"
using namespace SpreadsheetGui;
LineEdit::LineEdit(QWidget *parent)
: Gui::ExpressionLineEdit(parent, false, true)
, current()
, deltaCol(0)
, deltaRow(0)
, lastKeyPressed(0)
, lastModifiers(0)
{
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
}
bool LineEdit::eventFilter(QObject* object, QEvent* event)
{
if (event && event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling -- must be done via a QApplication event filter, otherwise the widget
// system will always grab the tab events
if (completerActive()) {
hideCompleter();
event->accept();
return true; // To make sure this tab press doesn't do anything else
}
else {
lastKeyPressed = keyEvent->key();
lastModifiers = keyEvent->modifiers();
}
}
}
return false; // We don't usually actually "handle" the tab event, we just keep track of it
}
bool LineEdit::event(QEvent *event)
{
if (event && event->type() == QEvent::KeyPress) {
if (event && event->type() == QEvent::FocusIn) {
qApp->installEventFilter(this);
}
else if (event && event->type() == QEvent::FocusOut) {
qApp->removeEventFilter(this);
if (lastKeyPressed)
Q_EMIT finishedWithKey(lastKeyPressed, lastModifiers);
lastKeyPressed = 0;
}
else if (event && event->type() == QEvent::KeyPress && !completerActive()) {
QKeyEvent * kevent = static_cast<QKeyEvent*>(event);
if (kevent->key() == Qt::Key_Tab) {
if (kevent->modifiers() == 0) {
deltaCol = 1;
deltaRow = 0;
Q_EMIT returnPressed();
return true;
}
}
else if (kevent->key() == Qt::Key_Backtab) {
if (kevent->modifiers() == Qt::ShiftModifier) {
deltaCol = -1;
deltaRow = 0;
Q_EMIT returnPressed();
return true;
}
}
else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
if (kevent->modifiers() == 0) {
deltaCol = 0;
deltaRow = 1;
Q_EMIT returnPressed();
return true;
}
else if (kevent->modifiers() == Qt::ShiftModifier) {
deltaCol = 0;
deltaRow = -1;
Q_EMIT returnPressed();
return true;
}
}
lastKeyPressed = kevent->key();
lastModifiers = kevent->modifiers();
}
return Gui::ExpressionLineEdit::event(event);
}
void LineEdit::setIndex(QModelIndex _current)
{
current = _current;
}
QModelIndex LineEdit::next() const
{
const QAbstractItemModel * m = current.model();
return m->index(qMin(qMax(0, current.row() + deltaRow), m->rowCount() - 1 ),
qMin(qMax(0, current.column() + deltaCol), m->columnCount() - 1 ) );
}
#include "moc_LineEdit.cpp"

View File

@@ -36,13 +36,17 @@ public:
explicit LineEdit(QWidget *parent = 0);
bool event(QEvent *event);
void setIndex(QModelIndex _current);
QModelIndex next() const;
Q_SIGNALS:
void finishedWithKey(int key, Qt::KeyboardModifiers modifiers);
private:
QModelIndex current;
int deltaCol;
int deltaRow;
bool eventFilter(QObject* object, QEvent* event);
private:
int lastKeyPressed;
Qt::KeyboardModifiers lastModifiers;
};
}

View File

@@ -101,6 +101,7 @@ static std::pair<int, int> selectedMinMaxColumns(QModelIndexList list)
SheetTableView::SheetTableView(QWidget *parent)
: QTableView(parent)
, sheet(0)
, tabCounter(0)
{
setHorizontalHeader(new SheetViewHeader(this,Qt::Horizontal));
setVerticalHeader(new SheetViewHeader(this,Qt::Vertical));
@@ -319,7 +320,7 @@ void SheetTableView::insertColumnsAfter()
{
assert(sheet != 0);
const auto columns = selectionModel()->selectedColumns();
const auto & [min, max] = selectedMinMaxColumns(columns);
const auto& [min, max] = selectedMinMaxColumns(columns);
assert(max - min == columns.size() - 1);
Q_UNUSED(min)
@@ -345,7 +346,7 @@ void SheetTableView::removeColumns()
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove rows"));
for (std::vector<int>::const_iterator it = sortedColumns.begin(); it != sortedColumns.end(); ++it)
Gui::cmdAppObjectArgs(sheet, "removeColumns('%s', %d)",
columnName(*it).c_str(), 1);
columnName(*it).c_str(), 1);
Gui::Command::commitCommand();
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
}
@@ -365,7 +366,7 @@ void SheetTableView::updateCellSpan(CellAddress address)
setSpan(address.row(), address.col(), rows, cols);
}
void SheetTableView::setSheet(Sheet * _sheet)
void SheetTableView::setSheet(Sheet* _sheet)
{
sheet = _sheet;
cellSpanChangedConnection = sheet->cellSpanChanged.connect(bind(&SheetTableView::updateCellSpan, this, bp::_1));
@@ -399,57 +400,45 @@ void SheetTableView::setSheet(Sheet * _sheet)
}
void SheetTableView::commitData ( QWidget * editor )
void SheetTableView::commitData(QWidget* editor)
{
QTableView::commitData(editor);
}
bool SheetTableView::edit ( const QModelIndex & index, EditTrigger trigger, QEvent * event )
bool SheetTableView::edit(const QModelIndex& index, EditTrigger trigger, QEvent* event)
{
if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed) )
if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed))
currentEditIndex = index;
return QTableView::edit(index, trigger, event);
}
bool SheetTableView::event(QEvent *event)
bool SheetTableView::event(QEvent* event)
{
/* Catch key presses for navigating the table; Enter/Return (+Shift), and Tab (+Shift) */
if (event && event->type() == QEvent::KeyPress) {
QKeyEvent * kevent = static_cast<QKeyEvent*>(event);
if (kevent->key() == Qt::Key_Tab) {
QModelIndex c = currentIndex();
if (kevent->modifiers() == 0) {
setCurrentIndex(model()->index(c.row(), qMin(c.column() + 1, model()->columnCount() -1)));
return true;
}
}
else if (kevent->key() == Qt::Key_Backtab) {
QModelIndex c = currentIndex();
if (kevent->modifiers() == Qt::ShiftModifier) {
setCurrentIndex(model()->index(c.row(), qMax(c.column() - 1, 0)));
return true;
}
}
else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
QModelIndex c = currentIndex();
if (kevent->modifiers() == 0) {
setCurrentIndex(model()->index(qMin(c.row() + 1, model()->rowCount() - 1), c.column()));
return true;
}
else if (kevent->modifiers() == Qt::ShiftModifier) {
setCurrentIndex(model()->index(qMax(c.row() - 1, 0), c.column()));
return true;
}
}
else if (kevent->key() == Qt::Key_Delete) {
if (event && event->type() == QEvent::KeyPress && this->hasFocus()) {
// If this widget has focus, look for keyboard events that represent movement shortcuts
// and handle them.
QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
switch (kevent->key()) {
case Qt::Key_Return: [[fallthrough]];
case Qt::Key_Enter: [[fallthrough]];
case Qt::Key_Home: [[fallthrough]];
case Qt::Key_End: [[fallthrough]];
case Qt::Key_Left: [[fallthrough]];
case Qt::Key_Right: [[fallthrough]];
case Qt::Key_Up: [[fallthrough]];
case Qt::Key_Down: [[fallthrough]];
case Qt::Key_Tab: [[fallthrough]];
case Qt::Key_Backtab:
finishEditWithMove(kevent->key(), kevent->modifiers(), true);
return true;
// Also handle the delete key here:
case Qt::Key_Delete:
deleteSelection();
return true;
default:
break;
}
else if (kevent->matches(QKeySequence::Cut)) {
if (kevent->matches(QKeySequence::Cut)) {
cutSelection();
return true;
}
@@ -468,18 +457,19 @@ bool SheetTableView::event(QEvent *event)
kevent->modifiers() == Qt::ShiftModifier ||
kevent->modifiers() == Qt::KeypadModifier) {
switch (kevent->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
case Qt::Key_Delete:
case Qt::Key_Home:
case Qt::Key_End:
case Qt::Key_Backspace:
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Return: [[fallthrough]];
case Qt::Key_Enter: [[fallthrough]];
case Qt::Key_Delete: [[fallthrough]];
case Qt::Key_Home: [[fallthrough]];
case Qt::Key_End: [[fallthrough]];
case Qt::Key_Backspace: [[fallthrough]];
case Qt::Key_Left: [[fallthrough]];
case Qt::Key_Right: [[fallthrough]];
case Qt::Key_Up: [[fallthrough]];
case Qt::Key_Down: [[fallthrough]];
case Qt::Key_Tab:
kevent->accept();
break;
default:
break;
}
@@ -614,13 +604,239 @@ void SheetTableView::pasteClipboard()
}
}
void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
void SheetTableView::finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion)
{
SpreadsheetGui::LineEdit * le = qobject_cast<SpreadsheetGui::LineEdit*>(editor);
// A utility lambda for finding the beginning and ending of data regions
auto scanForRegionBoundary = [this](int& r, int& c, int dr, int dc) {
auto startAddress = CellAddress(r, c);
auto startCell = sheet->getCell(startAddress);
bool startedAtEmptyCell = startCell ? !startCell->isUsed() : true;
const int maxRow = this->model()->rowCount() - 1;
const int maxCol = this->model()->columnCount() - 1;
while (c + dc >= 0 && r + dr >= 0 && c + dc <= maxCol && r + dr <= maxRow) {
r += dr;
c += dc;
auto cell = sheet->getCell(CellAddress(r, c));
auto cellIsEmpty = cell ? !cell->isUsed() : true;
if (cellIsEmpty && !startedAtEmptyCell) {
// Don't stop at the empty cell, stop at the last non-empty cell
r -= dr;
c -= dc;
break;
}
else if (!cellIsEmpty && startedAtEmptyCell) {
break;
}
}
if (r == startAddress.row() && c == startAddress.col()) {
// Always move at least one cell:
r += dr;
c += dc;
}
r = std::max(0, std::min(r, maxRow));
c = std::max(0, std::min(c, maxCol));
};
currentEditIndex = QModelIndex();
int targetRow = currentIndex().row();
int targetColumn = currentIndex().column();
int colSpan;
int rowSpan;
sheet->getSpans(CellAddress(targetRow, targetColumn), rowSpan, colSpan);
switch (keyPressed) {
case Qt::Key_Return:
case Qt::Key_Enter:
if (modifiers == Qt::NoModifier) {
targetRow += rowSpan;
targetColumn -= tabCounter;
}
else if (modifiers == Qt::ShiftModifier) {
targetRow -= 1;
targetColumn -= tabCounter;
}
else {
// For an unrecognized modifier, just go down
targetRow += rowSpan;
}
tabCounter = 0;
break;
case Qt::Key_Home:
// Home: row 1, same column
// Ctrl-Home: row 1, column 1
targetRow = 0;
if (modifiers == Qt::ControlModifier)
targetColumn = 0;
tabCounter = 0;
break;
case Qt::Key_End:
{
// End should take you to the last occupied cell in the current column
// Ctrl-End takes you to the last cell in the sheet
auto usedCells = sheet->getCells()->getUsedCells();
for (const auto& cell : usedCells) {
if (modifiers == Qt::NoModifier) {
if (cell.col() == targetColumn)
targetRow = std::max(targetRow, cell.row());
}
else if (modifiers == Qt::ControlModifier) {
targetRow = std::max(targetRow, cell.row());
targetColumn = std::max(targetColumn, cell.col());
}
}
tabCounter = 0;
break;
}
case Qt::Key_Left:
if (targetColumn == 0)
break; // Nothing to do, we're already in the first column
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
targetColumn--;
else if (modifiers == Qt::ControlModifier ||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
scanForRegionBoundary(targetRow, targetColumn, 0, -1);
else
targetColumn--; //Unrecognized modifier combination: default to just moving one cell
tabCounter = 0;
break;
case Qt::Key_Right:
if (targetColumn >= this->model()->columnCount() - 1)
break; // Nothing to do, we're already in the last column
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
targetColumn += colSpan;
else if (modifiers == Qt::ControlModifier ||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
scanForRegionBoundary(targetRow, targetColumn, 0, 1);
else
targetColumn += colSpan; //Unrecognized modifier combination: default to just moving one cell
tabCounter = 0;
break;
case Qt::Key_Up:
if (targetRow == 0)
break; // Nothing to do, we're already in the first column
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
targetRow--;
else if (modifiers == Qt::ControlModifier ||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
scanForRegionBoundary(targetRow, targetColumn, -1, 0);
else
targetRow--; //Unrecognized modifier combination: default to just moving one cell
tabCounter = 0;
break;
case Qt::Key_Down:
if (targetRow >= this->model()->rowCount() - 1)
break; // Nothing to do, we're already in the last row
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
targetRow += rowSpan;
else if (modifiers == Qt::ControlModifier ||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
scanForRegionBoundary(targetRow, targetColumn, 1, 0);
else
targetRow += rowSpan; //Unrecognized modifier combination: default to just moving one cell
tabCounter = 0;
break;
case Qt::Key_Tab:
if (modifiers == Qt::NoModifier) {
tabCounter++;
if (handleTabMotion)
targetColumn += colSpan;
}
else if (modifiers == Qt::ShiftModifier) {
tabCounter = 0;
if (handleTabMotion)
targetColumn--;
}
break;
case Qt::Key_Backtab:
if (modifiers == Qt::NoModifier) {
targetColumn--;
}
tabCounter = 0;
break;
default:
break;
}
if (this->sheet->isMergedCell(CellAddress(targetRow, targetColumn))) {
auto anchor = this->sheet->getAnchor(CellAddress(targetRow, targetColumn));
targetRow = anchor.row();
targetColumn = anchor.col();
}
// Overflow/underflow protection:
const int maxRow = this->model()->rowCount() - 1;
const int maxCol = this->model()->columnCount() - 1;
targetRow = std::max(0, std::min(targetRow, maxRow));
targetColumn = std::max(0, std::min(targetColumn, maxCol));
if (!(modifiers & Qt::ShiftModifier) || keyPressed == Qt::Key_Tab || keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
// We have to use this method so that Ctrl-modifier combinations don't result in multiple selection
this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetColumn),
QItemSelectionModel::ClearAndSelect);
}
else if (modifiers & Qt::ShiftModifier) {
// With shift down, this motion becomes a block selection command, rather than just simple motion:
ModifyBlockSelection(targetRow, targetColumn);
}
}
void SheetTableView::ModifyBlockSelection(int targetRow, int targetCol)
{
int startingRow = currentIndex().row();
int startingCol = currentIndex().column();
// Get the current block selection size:
auto selection = this->selectionModel()->selection();
for (const auto& range : selection) {
if (range.contains(currentIndex())) {
// This range contains the current cell, so it's the one we're going to modify (assuming we're at one of the corners)
int rangeMinRow = range.top();
int rangeMaxRow = range.bottom();
int rangeMinCol = range.left();
int rangeMaxCol = range.right();
if ((startingRow == rangeMinRow || startingRow == rangeMaxRow) &&
(startingCol == rangeMinCol || startingCol == rangeMaxCol)) {
if (range.contains(model()->index(targetRow, targetCol))) {
// If the range already contains the target cell, then we're making the range smaller
if (startingRow == rangeMinRow)
rangeMinRow = targetRow;
if (startingRow == rangeMaxRow)
rangeMaxRow = targetRow;
if (startingCol == rangeMinCol)
rangeMinCol = targetCol;
if (startingCol == rangeMaxCol)
rangeMaxCol = targetCol;
}
else {
// We're making the range bigger
rangeMinRow = std::min(rangeMinRow, targetRow);
rangeMaxRow = std::max(rangeMaxRow, targetRow);
rangeMinCol = std::min(rangeMinCol, targetCol);
rangeMaxCol = std::max(rangeMaxCol, targetCol);
}
QItemSelection oldRange(range.topLeft(), range.bottomRight());
this->selectionModel()->select(oldRange, QItemSelectionModel::Deselect);
QItemSelection newRange(model()->index(rangeMinRow, rangeMinCol), model()->index(rangeMaxRow, rangeMaxCol));
this->selectionModel()->select(newRange, QItemSelectionModel::Select);
}
break;
}
}
this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetCol), QItemSelectionModel::Current);
}
void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
{
QTableView::closeEditor(editor, hint);
setCurrentIndex(le->next());
}
void SheetTableView::mousePressEvent(QMouseEvent* event)
{
tabCounter = 0;
QTableView::mousePressEvent(event);
}
void SheetTableView::edit ( const QModelIndex & index )

View File

@@ -62,6 +62,9 @@ public:
void copySelection();
void cutSelection();
void pasteClipboard();
void finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion = false);
void ModifyBlockSelection(int targetRow, int targetColumn);
protected Q_SLOTS:
void commitData(QWidget *editor);
@@ -77,8 +80,10 @@ protected:
bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event);
bool event(QEvent *event);
void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint);
void mousePressEvent(QMouseEvent* event);
QModelIndex currentEditIndex;
int tabCounter;
Spreadsheet::Sheet * sheet;
boost::signals2::scoped_connection cellSpanChangedConnection;

View File

@@ -47,28 +47,11 @@ QWidget *SpreadsheetDelegate::createEditor(QWidget *parent,
const QModelIndex &index) const
{
SpreadsheetGui::LineEdit *editor = new SpreadsheetGui::LineEdit(parent);
editor->setIndex(index);
editor->setDocumentObject(sheet);
connect(editor, SIGNAL(returnPressed()), this, SLOT(commitAndCloseEditor()));
connect(editor, &SpreadsheetGui::LineEdit::finishedWithKey, this, &SpreadsheetDelegate::on_editorFinishedWithKey);
return editor;
}
void SpreadsheetDelegate::commitAndCloseEditor()
{
Gui::ExpressionLineEdit *editor = qobject_cast<Gui::ExpressionLineEdit *>(sender());
if (editor->completerActive()) {
editor->hideCompleter();
return;
}
// See https://forum.freecadweb.org/viewtopic.php?f=3&t=41694
// It looks like the slot commitAndCloseEditor() is not needed any more and even
// causes a crash when doing so because the LineEdit is still accessed after its destruction.
//Q_EMIT commitData(editor);
//Q_EMIT closeEditor(editor);
}
void SpreadsheetDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
@@ -89,6 +72,11 @@ void SpreadsheetDelegate::setModelData(QWidget *editor,
}
}
void SpreadsheetDelegate::on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers)
{
Q_EMIT finishedWithKey(key, modifiers);
}
QSize SpreadsheetDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
Q_UNUSED(option);

View File

@@ -45,8 +45,10 @@ public:
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
Q_SIGNALS:
void finishedWithKey(int key, Qt::KeyboardModifiers modifiers);
private Q_SLOTS:
void commitAndCloseEditor();
void on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers);
private:
Spreadsheet::Sheet * sheet;
};

View File

@@ -98,9 +98,12 @@ SheetView::SheetView(Gui::Document *pcDocument, App::DocumentObject *docObj, QWi
connect(ui->cells->verticalHeader(), SIGNAL(sectionResized ( int, int, int ) ),
this, SLOT(rowResized(int, int, int)));
connect(ui->cellContent, SIGNAL(returnPressed()), this, SLOT( editingFinished() ));
connect(ui->cellAlias, SIGNAL(returnPressed()), this, SLOT( editingFinished() ));
connect(ui->cellAlias, SIGNAL(textEdited(QString)), this, SLOT(aliasChanged(QString)));
connect(delegate, &SpreadsheetDelegate::finishedWithKey, this, &SheetView::editingFinishedWithKey);
connect(ui->cellContent, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmContentChanged(ui->cellContent->text()); });
connect(ui->cellContent, &LineEdit::returnPressed, this, [this]() {confirmContentChanged(ui->cellContent->text()); });
connect(ui->cellAlias, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmAliasChanged(ui->cellAlias->text()); });
connect(ui->cellAlias, &LineEdit::returnPressed, this, [this]() {confirmAliasChanged(ui->cellAlias->text()); });
connect(ui->cellAlias, &LineEdit::textEdited, this, &SheetView::aliasChanged);
columnWidthChangedConnection = sheet->columnWidthChanged.connect(bind(&SheetView::resizeColumn, this, bp::_1, bp::_2));
rowHeightChangedConnection = sheet->rowHeightChanged.connect(bind(&SheetView::resizeRow, this, bp::_1, bp::_2));
@@ -215,20 +218,6 @@ void SheetView::setCurrentCell(QString str)
updateAliasLine();
}
void SheetView::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Delete) {
if (event->modifiers() == 0) {
//model()->setData(currentIndex(), QVariant(), Qt::EditRole);
}
else if (event->modifiers() == Qt::ControlModifier) {
//model()->setData(currentIndex(), QVariant(), Qt::EditRole);
}
}
else
Gui::MDIView::keyPressEvent(event);
}
void SheetView::updateContentLine()
{
QModelIndex i = ui->cells->currentIndex();
@@ -238,7 +227,6 @@ void SheetView::updateContentLine()
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column())))
(void)cell->getStringContent(str);
ui->cellContent->setText(QString::fromUtf8(str.c_str()));
ui->cellContent->setIndex(i);
ui->cellContent->setEnabled(true);
// Update completer model; for the time being, we do this by setting the document object of the input line.
@@ -255,7 +243,6 @@ void SheetView::updateAliasLine()
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column())))
(void)cell->getAlias(str);
ui->cellAlias->setText(QString::fromUtf8(str.c_str()));
ui->cellAlias->setIndex(i);
ui->cellAlias->setEnabled(true);
// Update completer model; for the time being, we do this by setting the document object of the input line.
@@ -322,51 +309,53 @@ void SheetView::resizeRow(int col, int newSize)
ui->cells->setRowHeight(col, newSize);
}
void SheetView::editingFinished()
void SheetView::editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers)
{
if (ui->cellContent->completerActive()) {
ui->cellContent->hideCompleter();
return;
}
if (ui->cellAlias->completerActive()) {
ui->cellAlias->hideCompleter();
return;
}
QModelIndex i = ui->cells->currentIndex();
if (i.isValid()) {
QString str = ui->cellAlias->text();
bool aliasOkay = true;
if (str.length()!= 0 && !sheet->isValidAlias(Base::Tools::toStdString(str))){
aliasOkay = false;
}
ui->cellAlias->setDocumentObject(sheet);
ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole);
ui->cells->finishEditWithMove(key, modifiers);
}
}
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column()))){
if (!aliasOkay){
//do not show error message if failure to set new alias is because it is already the same string
std::string current_alias;
(void)cell->getAlias(current_alias);
if (str != QString::fromUtf8(current_alias.c_str())){
Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(str).c_str());
}
} else {
std::string address = CellAddress(i.row(), i.column()).toString();
Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')",
address, str.toStdString());
Gui::cmdAppDocument(sheet->getDocument(), "recompute()");
void SheetView::confirmAliasChanged(const QString& text)
{
bool aliasOkay = true;
ui->cellAlias->setDocumentObject(sheet);
if (text.length() != 0 && !sheet->isValidAlias(Base::Tools::toStdString(text))) {
aliasOkay = false;
}
QModelIndex i = ui->cells->currentIndex();
if (const auto* cell = sheet->getCell(CellAddress(i.row(), i.column()))) {
if (!aliasOkay) {
//do not show error message if failure to set new alias is because it is already the same string
std::string current_alias;
(void)cell->getAlias(current_alias);
if (text != QString::fromUtf8(current_alias.c_str())) {
Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(text).c_str());
}
}
ui->cells->setCurrentIndex(ui->cellContent->next());
ui->cells->setFocus();
else {
std::string address = CellAddress(i.row(), i.column()).toString();
Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')",
address, text.toStdString());
Gui::cmdAppDocument(sheet->getDocument(), "recompute()");
ui->cells->setFocus();
}
}
}
void SheetView::confirmContentChanged(const QString& text)
{
QModelIndex i = ui->cells->currentIndex();
ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole);
ui->cells->setFocus();
}
void SheetView::aliasChanged(const QString& text)
{
// check live the input and highlight if the user input invalid characters

View File

@@ -82,8 +82,10 @@ public:
virtual void deleteSelf();
protected Q_SLOTS:
void editingFinished();
void editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers);
void confirmAliasChanged(const QString& text);
void aliasChanged(const QString& text);
void confirmContentChanged(const QString& text);
void currentChanged( const QModelIndex & current, const QModelIndex & previous );
void columnResized(int col, int oldSize, int newSize);
void rowResized(int row, int oldSize, int newSize);
@@ -94,7 +96,6 @@ protected:
void updateContentLine();
void updateAliasLine();
void setCurrentCell(QString str);
void keyPressEvent(QKeyEvent *event);
void resizeColumn(int col, int newSize);
void resizeRow(int col, int newSize);