fix tablet behavior on osx

This commit is contained in:
shun-iwasawa 2017-12-06 13:03:45 +09:00 committed by shun_iwasawa
parent 7c12f89dd4
commit 074b3ed36a
4 changed files with 125 additions and 69 deletions

View file

@ -484,8 +484,7 @@ void EraserTool::startErase(
m_indexes.resize(size); m_indexes.resize(size);
for (UINT i = 0; i < size; i++) m_indexes[i] = i; for (UINT i = 0; i < size; i++) m_indexes[i] = i;
assert(m_undo == 0); if (m_undo) delete m_undo;
delete m_undo;
TXshSimpleLevel *level = TXshSimpleLevel *level =
TTool::getApplication()->getCurrentLevel()->getSimpleLevel(); TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
m_undo = new UndoEraser(level, getCurrentFid()); m_undo = new UndoEraser(level, getCurrentFid());

View file

@ -444,10 +444,7 @@ SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent)
, m_mouseButton(Qt::NoButton) , m_mouseButton(Qt::NoButton)
, m_foregroundDrawing(false) , m_foregroundDrawing(false)
, m_tabletEvent(false) , m_tabletEvent(false)
, m_tabletActive(false)
, m_tabletMove(false) , m_tabletMove(false)
, m_tabletPressed(false)
, m_tabletReleased(false)
, m_buttonClicked(false) , m_buttonClicked(false)
, m_referenceMode(NORMAL_REFERENCE) , m_referenceMode(NORMAL_REFERENCE)
, m_previewMode(NO_PREVIEW) , m_previewMode(NO_PREVIEW)
@ -491,8 +488,10 @@ SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent)
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
setAcceptDrops(true); setAcceptDrops(true);
this->setMouseTracking(true); this->setMouseTracking(true);
// to be introduced from Qt 5.9 // introduced from Qt 5.9
// this->setTabletTracking(true); #if QT_VERSION >= 0x050900
this->setTabletTracking(true);
#endif
for (int i = 0; i < tArrayCount(m_viewAff); i++) for (int i = 0; i < tArrayCount(m_viewAff); i++)
setViewMatrix(getNormalZoomScale(), i); setViewMatrix(getNormalZoomScale(), i);

View file

@ -69,8 +69,13 @@ class SceneViewer final : public GLWidgetForHighDpi,
QPoint m_pos; QPoint m_pos;
Qt::MouseButton m_mouseButton; Qt::MouseButton m_mouseButton;
bool m_foregroundDrawing; bool m_foregroundDrawing;
bool m_tabletEvent, m_tabletPressed, m_tabletReleased, m_tabletMove, bool m_tabletEvent, m_tabletMove;
m_tabletActive; enum TabletState {
None = 0,
Touched,
OnStroke,
Released
} m_tabletState = None;
// used to handle wrong mouse drag events! // used to handle wrong mouse drag events!
bool m_buttonClicked, m_toolSwitched; bool m_buttonClicked, m_toolSwitched;
bool m_shownOnce = false; bool m_shownOnce = false;
@ -310,6 +315,9 @@ protected:
void onPress(const TMouseEvent &event); void onPress(const TMouseEvent &event);
void onMove(const TMouseEvent &event); void onMove(const TMouseEvent &event);
void onRelease(const TMouseEvent &event); void onRelease(const TMouseEvent &event);
void onContextMenu(const QPoint &pos, const QPoint &globalPos);
void onEnter();
void onLeave();
void wheelEvent(QWheelEvent *) override; void wheelEvent(QWheelEvent *) override;
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;

View file

@ -222,15 +222,16 @@ void SceneViewer::onButtonPressed(FlipConsole::EGadget button) {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// when using tablet on OSX, use tabletEvent instead of all the mouse-related
// events
// on Windows or on other OS, handle only left-button case
void SceneViewer::tabletEvent(QTabletEvent *e) { void SceneViewer::tabletEvent(QTabletEvent *e) {
if (m_freezedStatus != NO_FREEZED) return; if (m_freezedStatus != NO_FREEZED) return;
m_tabletEvent = true; m_tabletEvent = true;
m_pressure = e->pressure(); m_pressure = e->pressure();
m_tabletPressed = false; m_tabletMove = false;
m_tabletReleased = false;
m_tabletMove = false;
// Management of the Eraser pointer // Management of the Eraser pointer
ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); ToolHandle *toolHandle = TApp::instance()->getCurrentTool();
if (e->pointerType() == QTabletEvent::Eraser) { if (e->pointerType() == QTabletEvent::Eraser) {
@ -252,11 +253,23 @@ void SceneViewer::tabletEvent(QTabletEvent *e) {
} }
switch (e->type()) { switch (e->type()) {
case QEvent::TabletPress: { case QEvent::TabletPress: {
#ifdef MACOSX
// In OSX tablet action may cause only tabletEvent, not followed by // In OSX tablet action may cause only tabletEvent, not followed by
// mousePressEvent. // mousePressEvent.
// So call onPress here in order to enable processing. // So call onPress here in order to enable processing.
// This separation done is only for the left Button (regular pen touch), if (e->button() == Qt::LeftButton) m_tabletState = Touched;
// because TMouseEvent mouseEvent;
initToonzEvent(mouseEvent, e, height(), m_pressure, getDevPixRatio());
onPress(mouseEvent);
// create context menu on right click here
if (e->button() == Qt::RightButton) {
m_mouseButton = Qt::NoButton;
onContextMenu(e->pos(), e->globalPos());
}
#else
// for Windows, use tabletEvent only for the left Button
// (regular pen touch), because
// the current Qt seems to fail to catch the Wacom's button binding properly // the current Qt seems to fail to catch the Wacom's button binding properly
// with QTabletEvent->button() when pressing middle or right button. // with QTabletEvent->button() when pressing middle or right button.
// So, in such case set m_tabletEvent = FALSE and let the mousePressEvent to // So, in such case set m_tabletEvent = FALSE and let the mousePressEvent to
@ -264,39 +277,69 @@ void SceneViewer::tabletEvent(QTabletEvent *e) {
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
TMouseEvent mouseEvent; TMouseEvent mouseEvent;
initToonzEvent(mouseEvent, e, height(), e->pressure(), getDevPixRatio()); initToonzEvent(mouseEvent, e, height(), e->pressure(), getDevPixRatio());
m_tabletPressed = true; m_tabletState = Touched;
onPress(mouseEvent); onPress(mouseEvent);
} else } else
m_tabletEvent = false; m_tabletEvent = false;
#endif
} break; } break;
case QEvent::TabletRelease: { case QEvent::TabletRelease: {
if (m_tabletActive) { #ifdef MACOSX
m_tabletReleased = true; if (m_tabletState == OnStroke) m_tabletState = Released;
TMouseEvent mouseEvent;
initToonzEvent(mouseEvent, e, height(), m_pressure, getDevPixRatio());
onRelease(mouseEvent);
if (TApp::instance()->getCurrentTool()->isToolBusy())
TApp::instance()->getCurrentTool()->setToolBusy(false);
#else
if (m_tabletState == OnStroke) {
m_tabletState = Released;
TMouseEvent mouseEvent; TMouseEvent mouseEvent;
initToonzEvent(mouseEvent, e, height(), e->pressure(), getDevPixRatio()); initToonzEvent(mouseEvent, e, height(), e->pressure(), getDevPixRatio());
onRelease(mouseEvent); onRelease(mouseEvent);
} else } else
m_tabletEvent = false; m_tabletEvent = false;
#endif
} break; } break;
// for now "TabletMove" will be called only when with some button.
// setTabletTracking(bool) will be introduced in Qt5.9
case QEvent::TabletMove: { case QEvent::TabletMove: {
if (m_tabletActive) { #ifdef MACOSX
QPoint curPos = e->pos() * getDevPixRatio(); // for now OSX seems to fail to call enter/leaveEvent properly while
// It seems that the tabletEvent is called more often than mouseMoveEvent. // the tablet is floating
// So I fire the interval timer in order to limit the following process bool isHoveringInsideViewer =
// to be called in 50fps in maximum. !rect().marginsRemoved(QMargins(5, 5, 5, 5)).contains(e->pos());
if (curPos != m_lastMousePos && !m_isBusyOnTabletMove) { // call the fake enter event
m_isBusyOnTabletMove = true; if (isHoveringInsideViewer) onEnter();
TMouseEvent mouseEvent; #else
initToonzEvent(mouseEvent, e, height(), e->pressure(), // for Windowsm, use tabletEvent only for the left Button
getDevPixRatio()); if (m_tabletState != OnStroke) {
m_tabletMove = true;
QTimer::singleShot(20, this, SLOT(releaseBusyOnTabletMove()));
onMove(mouseEvent);
}
} else
m_tabletEvent = false; m_tabletEvent = false;
break;
}
#endif
// cancel stroke to prevent drawing while floating
if (m_tabletState == OnStroke && m_pressure == 0.0) {
resetTabletStatus();
break;
}
QPoint curPos = e->pos() * getDevPixRatio();
// It seems that the tabletEvent is called more often than mouseMoveEvent.
// So I fire the interval timer in order to limit the following process
// to be called in 50fps in maximum.
if (curPos != m_lastMousePos && !m_isBusyOnTabletMove) {
m_isBusyOnTabletMove = true;
TMouseEvent mouseEvent;
initToonzEvent(mouseEvent, e, height(), m_pressure, getDevPixRatio());
m_tabletMove = true;
QTimer::singleShot(20, this, SLOT(releaseBusyOnTabletMove()));
onMove(mouseEvent);
}
#ifdef MACOSX
// call the fake leave event if the pen is hovering the viewer edge
if (!isHoveringInsideViewer) onLeave();
#endif
} break; } break;
default: default:
break; break;
@ -306,7 +349,11 @@ void SceneViewer::tabletEvent(QTabletEvent *e) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneViewer::leaveEvent(QEvent *) { void SceneViewer::leaveEvent(QEvent *) { onLeave(); }
//-----------------------------------------------------------------------------
void SceneViewer::onLeave() {
if (!m_isMouseEntered) return; if (!m_isMouseEntered) return;
m_isMouseEntered = false; m_isMouseEntered = false;
@ -319,7 +366,11 @@ void SceneViewer::leaveEvent(QEvent *) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneViewer::enterEvent(QEvent *) { void SceneViewer::enterEvent(QEvent *) { onEnter(); }
//-----------------------------------------------------------------------------
void SceneViewer::onEnter() {
if (m_isMouseEntered) return; if (m_isMouseEntered) return;
m_isMouseEntered = true; m_isMouseEntered = true;
@ -372,7 +423,6 @@ void SceneViewer::mouseMoveEvent(QMouseEvent *event) {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneViewer::onMove(const TMouseEvent &event) { void SceneViewer::onMove(const TMouseEvent &event) {
if (m_freezedStatus != NO_FREEZED) return; if (m_freezedStatus != NO_FREEZED) return;
@ -476,9 +526,7 @@ void SceneViewer::onMove(const TMouseEvent &event) {
// << " buttonClicked=" << m_buttonClicked; // << " buttonClicked=" << m_buttonClicked;
// separate tablet events from mouse events // separate tablet events from mouse events
// don't perform a drag event if tablet not active if (m_tabletEvent && m_tabletState == OnStroke && m_tabletMove) {
if (m_tabletActive && !m_tabletMove) return;
if (m_tabletEvent && m_tabletActive && m_tabletMove) {
tool->leftButtonDrag(pos, event); tool->leftButtonDrag(pos, event);
} }
@ -610,15 +658,14 @@ void SceneViewer::onPress(const TMouseEvent &event) {
pos.y /= m_dpiScale.y; pos.y /= m_dpiScale.y;
} }
// separate tablet and mouse events // separate tablet and mouse events
if (m_tabletEvent && m_tabletPressed) { if (m_tabletEvent && m_tabletState == Touched) {
m_tabletActive = true; m_tabletState = OnStroke;
tool->leftButtonDown(pos, event); tool->leftButtonDown(pos, event);
} else if (m_mouseButton == Qt::LeftButton) { } else if (m_mouseButton == Qt::LeftButton) {
TApp::instance()->getCurrentTool()->setToolBusy(true); TApp::instance()->getCurrentTool()->setToolBusy(true);
tool->leftButtonDown(pos, event); tool->leftButtonDown(pos, event);
} }
if (m_mouseButton == Qt::RightButton) tool->rightButtonDown(pos, event); if (m_mouseButton == Qt::RightButton) tool->rightButtonDown(pos, event);
m_tabletPressed = false;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -661,8 +708,6 @@ void SceneViewer::onRelease(const TMouseEvent &event) {
if (m_mouseButton != event.button()) return; if (m_mouseButton != event.button()) return;
// reject if tablet was active and the up button is not actually the pen.
if (m_tabletActive && !m_tabletReleased) return;
if (m_current3DDevice != NONE) { if (m_current3DDevice != NONE) {
m_mouseButton = Qt::NoButton; m_mouseButton = Qt::NoButton;
m_tabletEvent = false; m_tabletEvent = false;
@ -701,19 +746,17 @@ void SceneViewer::onRelease(const TMouseEvent &event) {
pos.y /= m_dpiScale.y; pos.y /= m_dpiScale.y;
} }
if (m_mouseButton == Qt::LeftButton || m_tabletReleased) { if (m_mouseButton == Qt::LeftButton || m_tabletState == Released) {
if (!m_toolSwitched) tool->leftButtonUp(pos, event); if (!m_toolSwitched) tool->leftButtonUp(pos, event);
TApp::instance()->getCurrentTool()->setToolBusy(false); TApp::instance()->getCurrentTool()->setToolBusy(false);
} }
} }
quit: quit:
m_mouseButton = Qt::NoButton; m_mouseButton = Qt::NoButton;
m_tabletPressed = false; m_tabletState = None;
m_tabletActive = false; m_tabletMove = false;
m_tabletReleased = false; m_pressure = 0;
m_tabletMove = false;
m_pressure = 0;
// Leave m_tabletEvent as-is in order to check whether the onRelease is called // Leave m_tabletEvent as-is in order to check whether the onRelease is called
// from tabletEvent or not in mouseReleaseEvent. // from tabletEvent or not in mouseReleaseEvent.
} }
@ -728,14 +771,12 @@ quit:
// QTBUG-53628. // QTBUG-53628.
void SceneViewer::resetTabletStatus() { void SceneViewer::resetTabletStatus() {
if (!m_buttonClicked) return; if (!m_buttonClicked) return;
m_mouseButton = Qt::NoButton; m_mouseButton = Qt::NoButton;
m_tabletEvent = false; m_tabletEvent = false;
m_tabletPressed = false; m_tabletState = None;
m_tabletActive = false; m_tabletMove = false;
m_tabletReleased = false; m_pressure = 0;
m_tabletMove = false; m_buttonClicked = false;
m_pressure = 0;
m_buttonClicked = false;
if (TApp::instance()->getCurrentTool()->isToolBusy()) if (TApp::instance()->getCurrentTool()->isToolBusy())
TApp::instance()->getCurrentTool()->setToolBusy(false); TApp::instance()->getCurrentTool()->setToolBusy(false);
} }
@ -1372,6 +1413,7 @@ using namespace ImageUtils;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SceneViewer::contextMenuEvent(QContextMenuEvent *e) { void SceneViewer::contextMenuEvent(QContextMenuEvent *e) {
if (m_tabletEvent) return;
#ifndef _WIN32 #ifndef _WIN32
/* On windows the widget receive the release event before the menu /* On windows the widget receive the release event before the menu
is shown, on linux and osx the release event is lost, never is shown, on linux and osx the release event is lost, never
@ -1382,12 +1424,19 @@ void SceneViewer::contextMenuEvent(QContextMenuEvent *e) {
QApplication::instance()->sendEvent(this, &fakeRelease); QApplication::instance()->sendEvent(this, &fakeRelease);
#endif #endif
onContextMenu(e->pos(), e->globalPos());
}
//-----------------------------------------------------------------------------
void SceneViewer::onContextMenu(const QPoint &pos, const QPoint &globalPos) {
if (m_freezedStatus != NO_FREEZED) return; if (m_freezedStatus != NO_FREEZED) return;
if (m_isLocator) return; if (m_isLocator) return;
static bool menuVisible = false;
if (menuVisible) return;
menuVisible = true;
int devPixRatio = getDevPixRatio(); int devPixRatio = getDevPixRatio();
TPoint winPos(e->pos().x() * devPixRatio, TPoint winPos(pos.x() * devPixRatio, height() - pos.y() * devPixRatio);
height() - e->pos().y() * devPixRatio);
std::vector<int> columnIndices; std::vector<int> columnIndices;
// enable to select all the columns regardless of the click position // enable to select all the columns regardless of the click position
for (int i = 0; for (int i = 0;
@ -1398,9 +1447,9 @@ void SceneViewer::contextMenuEvent(QContextMenuEvent *e) {
SceneViewerContextMenu *menu = new SceneViewerContextMenu(this); SceneViewerContextMenu *menu = new SceneViewerContextMenu(this);
TTool *tool = TApp::instance()->getCurrentTool()->getTool(); TTool *tool = TApp::instance()->getCurrentTool()->getTool();
TPointD pos = ((tool) ? tool->getMatrix().inv() : TAffine()) * TPointD p = ((tool) ? tool->getMatrix().inv() : TAffine()) *
winToWorld(e->pos() * devPixRatio); winToWorld(pos * devPixRatio);
menu->addEnterGroupCommands(pos); menu->addEnterGroupCommands(p);
menu->addLevelCommands(columnIndices); menu->addLevelCommands(columnIndices);
@ -1411,8 +1460,9 @@ void SceneViewer::contextMenuEvent(QContextMenuEvent *e) {
cvp->addShowHideContextMenu(menu); cvp->addShowHideContextMenu(menu);
} }
menu->exec(e->globalPos()); menu->exec(globalPos);
delete menu; delete menu;
menuVisible = false;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------