Implement Alt+Arrow key movement

Work on #92
This commit is contained in:
Rasmus Karlsson 2017-08-12 15:41:14 +02:00
parent a6573e83e0
commit ce61351fe3
6 changed files with 197 additions and 14 deletions

View file

@ -68,6 +68,8 @@ ChatWidget::ChatWidget(ChannelManager &_channelManager, NotebookPage *parent)
std::bind(&ChatWidget::channelNameUpdated, this, std::placeholders::_1)); std::bind(&ChatWidget::channelNameUpdated, this, std::placeholders::_1));
this->channelNameUpdated(this->channelName.getValue()); this->channelNameUpdated(this->channelName.getValue());
this->input.textInput.installEventFilter(parent);
} }
ChatWidget::~ChatWidget() ChatWidget::~ChatWidget()
@ -193,9 +195,14 @@ void ChatWidget::updateGifEmotes()
this->view.updateGifEmotes(); this->view.updateGifEmotes();
} }
void ChatWidget::giveFocus() void ChatWidget::giveFocus(Qt::FocusReason reason)
{ {
this->input.textInput.setFocus(); this->input.textInput.setFocus(reason);
}
bool ChatWidget::hasFocus() const
{
return this->input.textInput.hasFocus();
} }
void ChatWidget::paintEvent(QPaintEvent *) void ChatWidget::paintEvent(QPaintEvent *)

View file

@ -53,7 +53,8 @@ public:
void layoutMessages(bool forceUpdate = false); void layoutMessages(bool forceUpdate = false);
void updateGifEmotes(); void updateGifEmotes();
void giveFocus(); void giveFocus(Qt::FocusReason reason);
bool hasFocus() const;
pajlada::Settings::Setting<std::string> channelName; pajlada::Settings::Setting<std::string> channelName;

View file

@ -3,6 +3,7 @@
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "completionmanager.hpp" #include "completionmanager.hpp"
#include "ircmanager.hpp" #include "ircmanager.hpp"
#include "notebookpage.hpp"
#include "settingsmanager.hpp" #include "settingsmanager.hpp"
#include <QCompleter> #include <QCompleter>
@ -71,17 +72,61 @@ ChatWidgetInput::ChatWidgetInput(ChatWidget *_chatWidget)
} }
prevIndex = prevMsg.size(); prevIndex = prevMsg.size();
} else if (event->key() == Qt::Key_Up) { } else if (event->key() == Qt::Key_Up) {
if (prevMsg.size() && prevIndex) { if (event->modifiers() == Qt::AltModifier) {
prevIndex--; NotebookPage *page = static_cast<NotebookPage *>(this->chatWidget->parentWidget());
textInput.setText(prevMsg.at(prevIndex));
int reqX = page->currentX;
int reqY = page->lastRequestedY[reqX] - 1;
qDebug() << "Alt+Down to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
} else {
if (prevMsg.size() && prevIndex) {
prevIndex--;
textInput.setText(prevMsg.at(prevIndex));
}
} }
} else if (event->key() == Qt::Key_Down) { } else if (event->key() == Qt::Key_Down) {
if (prevIndex != (prevMsg.size() - 1) && prevIndex != prevMsg.size()) { if (event->modifiers() == Qt::AltModifier) {
prevIndex++; NotebookPage *page = static_cast<NotebookPage *>(this->chatWidget->parentWidget());
textInput.setText(prevMsg.at(prevIndex));
int reqX = page->currentX;
int reqY = page->lastRequestedY[reqX] + 1;
qDebug() << "Alt+Down to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
} else { } else {
prevIndex = prevMsg.size(); if (prevIndex != (prevMsg.size() - 1) && prevIndex != prevMsg.size()) {
textInput.setText(QString()); prevIndex++;
textInput.setText(prevMsg.at(prevIndex));
} else {
prevIndex = prevMsg.size();
textInput.setText(QString());
}
}
} else if (event->key() == Qt::Key_Left) {
if (event->modifiers() == Qt::AltModifier) {
NotebookPage *page = static_cast<NotebookPage *>(this->chatWidget->parentWidget());
int reqX = page->currentX - 1;
int reqY = page->lastRequestedY[reqX];
qDebug() << "Alt+Left to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
}
} else if (event->key() == Qt::Key_Right) {
if (event->modifiers() == Qt::AltModifier) {
NotebookPage *page = static_cast<NotebookPage *>(this->chatWidget->parentWidget());
int reqX = page->currentX + 1;
int reqY = page->lastRequestedY[reqX];
qDebug() << "Alt+Right to" << reqX << "/" << reqY;
page->requestFocus(reqX, reqY);
} }
} }
}); });
@ -148,7 +193,7 @@ void ChatWidgetInput::resizeEvent(QResizeEvent *)
void ChatWidgetInput::mousePressEvent(QMouseEvent *) void ChatWidgetInput::mousePressEvent(QMouseEvent *)
{ {
this->chatWidget->giveFocus(); this->chatWidget->giveFocus(Qt::MouseFocusReason);
} }
} // namespace widgets } // namespace widgets

View file

@ -339,7 +339,7 @@ void ChatWidgetView::mousePressEvent(QMouseEvent *event)
this->isMouseDown = true; this->isMouseDown = true;
this->lastPressPosition = event->screenPos(); this->lastPressPosition = event->screenPos();
this->chatWidget->giveFocus(); this->chatWidget->giveFocus(Qt::MouseFocusReason);
} }
void ChatWidgetView::mouseReleaseEvent(QMouseEvent *event) void ChatWidgetView::mouseReleaseEvent(QMouseEvent *event)

View file

@ -82,7 +82,7 @@ void NotebookPage::addToLayout(ChatWidget *widget,
std::pair<int, int> position = std::pair<int, int>(-1, -1)) std::pair<int, int> position = std::pair<int, int>(-1, -1))
{ {
this->chatWidgets.push_back(widget); this->chatWidgets.push_back(widget);
widget->giveFocus(); widget->giveFocus(Qt::MouseFocusReason);
// add vbox at the end // add vbox at the end
if (position.first < 0 || position.first >= this->ui.hbox.count()) { if (position.first < 0 || position.first >= this->ui.hbox.count()) {
@ -90,6 +90,7 @@ void NotebookPage::addToLayout(ChatWidget *widget,
vbox->addWidget(widget); vbox->addWidget(widget);
this->ui.hbox.addLayout(vbox, 1); this->ui.hbox.addLayout(vbox, 1);
this->refreshCurrentFocusCoordinates();
return; return;
} }
@ -99,6 +100,7 @@ void NotebookPage::addToLayout(ChatWidget *widget,
vbox->addWidget(widget); vbox->addWidget(widget);
this->ui.hbox.insertLayout(position.first, vbox, 1); this->ui.hbox.insertLayout(position.first, vbox, 1);
this->refreshCurrentFocusCoordinates();
return; return;
} }
@ -106,6 +108,8 @@ void NotebookPage::addToLayout(ChatWidget *widget,
auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(position.first)); auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(position.first));
vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget); vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
this->refreshCurrentFocusCoordinates();
} }
const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
@ -134,6 +138,103 @@ void NotebookPage::addChat(bool openChannelNameDialog)
this->addToLayout(w, std::pair<int, int>(-1, -1)); this->addToLayout(w, std::pair<int, int>(-1, -1));
} }
void NotebookPage::refreshCurrentFocusCoordinates(bool alsoSetLastRequested)
{
int setX = -1;
int setY = -1;
bool doBreak = false;
for (int x = 0; x < this->ui.hbox.count(); ++x) {
QLayoutItem *item = this->ui.hbox.itemAt(x);
if (item->isEmpty()) {
setX = x;
break;
}
QVBoxLayout *vbox = static_cast<QVBoxLayout *>(item->layout());
for (int y = 0; y < vbox->count(); ++y) {
QLayoutItem *innerItem = vbox->itemAt(y);
if (innerItem->isEmpty()) {
setX = x;
setY = y;
doBreak = true;
break;
}
QWidget *w = innerItem->widget();
if (w) {
ChatWidget *chatWidget = static_cast<ChatWidget *>(w);
if (chatWidget->hasFocus()) {
setX = x;
setY = y;
doBreak = true;
break;
}
}
}
if (doBreak) {
break;
}
}
if (setX != -1) {
this->currentX = setX;
if (setY != -1) {
this->currentY = setY;
if (alsoSetLastRequested) {
this->lastRequestedY[setX] = setY;
}
}
}
}
void NotebookPage::requestFocus(int requestedX, int requestedY)
{
// XXX: Perhaps if we request an Y coordinate out of bounds, we shuold set all previously set
// requestedYs to 0 (if -1 is requested) or that x-coordinates vbox count (if requestedY >=
// currentvbox.count() is requested)
if (requestedX < 0 || requestedX >= this->ui.hbox.count()) {
return;
}
QLayoutItem *item = this->ui.hbox.itemAt(requestedX);
QWidget *xW = item->widget();
if (item->isEmpty()) {
qDebug() << "Requested hbox item " << requestedX << "is empty";
if (xW) {
qDebug() << "but xW is not null";
// TODO: figure out what to do here
}
return;
}
QVBoxLayout *vbox = static_cast<QVBoxLayout *>(item->layout());
if (requestedY < 0) {
requestedY = 0;
} else if (requestedY >= vbox->count()) {
requestedY = vbox->count() - 1;
}
this->lastRequestedY[requestedX] = requestedY;
QLayoutItem *innerItem = vbox->itemAt(requestedY);
if (innerItem->isEmpty()) {
qDebug() << "Requested vbox item " << requestedY << "is empty";
return;
}
QWidget *w = innerItem->widget();
if (w) {
ChatWidget *chatWidget = static_cast<ChatWidget *>(w);
chatWidget->giveFocus(Qt::OtherFocusReason);
}
}
void NotebookPage::enterEvent(QEvent *) void NotebookPage::enterEvent(QEvent *)
{ {
if (this->ui.hbox.count() == 0) { if (this->ui.hbox.count() == 0) {
@ -240,6 +341,17 @@ void NotebookPage::dropEvent(QDropEvent *event)
this->dropPreview.hide(); this->dropPreview.hide();
} }
bool NotebookPage::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn) {
QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
this->refreshCurrentFocusCoordinates((focusEvent->reason() == Qt::MouseFocusReason));
}
return false;
}
void NotebookPage::paintEvent(QPaintEvent *) void NotebookPage::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -259,6 +371,14 @@ void NotebookPage::paintEvent(QPaintEvent *)
} }
} }
void NotebookPage::showEvent(QShowEvent *event)
{
// Whenever this notebook page is shown, give focus to the last focused chat widget
// If this is the first time this notebook page is shown, it will give focus to the top-left
// chat widget
this->requestFocus(this->currentX, this->currentY);
}
static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget) static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
{ {
for (int i = 0; i < layout->count(); ++i) { for (int i = 0; i < layout->count(); ++i) {

View file

@ -44,9 +44,19 @@ public:
static ChatWidget *draggingSplit; static ChatWidget *draggingSplit;
static std::pair<int, int> dropPosition; static std::pair<int, int> dropPosition;
int currentX = 0;
int currentY = 0;
std::map<int, int> lastRequestedY;
void refreshCurrentFocusCoordinates(bool alsoSetLastRequested = false);
void requestFocus(int x, int y);
protected: protected:
virtual bool eventFilter(QObject *object, QEvent *event) override;
virtual void paintEvent(QPaintEvent *) override; virtual void paintEvent(QPaintEvent *) override;
virtual void showEvent(QShowEvent *) override;
virtual void enterEvent(QEvent *) override; virtual void enterEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override; virtual void leaveEvent(QEvent *) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override;