Commit b180f3c7 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Merge remote-tracking branch 'origin/master' into kazakov/lazy-brush-remastered

parents c7e25d04 79366f6e
......@@ -716,7 +716,6 @@ for %%a in (%EXT_TARGETS%) do (
echo ERROR: Building of ext_%%a failed! 1>&2
exit /b 105
)
echo.
)
echo.
......
......@@ -204,6 +204,18 @@
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="ruler_pixel_multiple2">
<icon></icon>
<text>Use multiple of 2 for pixel scale</text>
<whatsThis>Use multiple of 2 for pixel scale</whatsThis>
<toolTip>Use multiple of 2 for pixel scale</toolTip>
<iconText>Use multiple of 2 for pixel scale</iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Painting">
......
......@@ -29,7 +29,6 @@
#include <SvgParser.h>
#include <QFileInfo>
#include <KoDocumentResourceManager.h>
#include <QXmlStreamReader>
#include "kis_debug.h"
......
......@@ -28,8 +28,6 @@
#include "KoOdfStylesReader.h"
#include <QXmlStreamReader>
class Q_DECL_HIDDEN KoOdfReadStore::Private
{
public:
......
......@@ -20,10 +20,7 @@
#include "KoXmlReader.h"
#include "KoXmlNS.h"
#include <QTextCodec>
#include <QTextDecoder>
#include <QXmlStreamReader>
#include <QStringList>
// ==================================================================
//
......
......@@ -705,6 +705,7 @@ void KisViewManager::createActions()
a = actionManager()->createAction("edit_blacklist_cleanup");
connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup()));
actionManager()->createAction("ruler_pixel_multiple2");
d->showRulersAction = actionManager()->createAction("view_ruler");
d->showRulersAction->setChecked(cfg.showRulers());
connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
......
......@@ -36,6 +36,8 @@ public:
: showGuides(false)
, snapToGuides(false)
, lockGuides(false)
, rulersMultiple2(false)
, unitType(KoUnit::Pixel)
{}
bool operator==(const Private &rhs) {
......@@ -45,7 +47,9 @@ public:
snapToGuides == rhs.snapToGuides &&
lockGuides == rhs.lockGuides &&
guidesColor == rhs.guidesColor &&
guidesLineType == rhs.guidesLineType;
guidesLineType == rhs.guidesLineType &&
rulersMultiple2 == rhs.rulersMultiple2 &&
unitType == rhs.unitType;
}
QList<qreal> horzGuideLines;
......@@ -54,6 +58,9 @@ public:
bool showGuides;
bool snapToGuides;
bool lockGuides;
bool rulersMultiple2;
KoUnit::Type unitType;
QColor guidesColor;
LineTypeInternal guidesLineType;
......@@ -149,6 +156,26 @@ void KisGuidesConfig::setSnapToGuides(bool value)
d->snapToGuides = value;
}
bool KisGuidesConfig::rulersMultiple2() const
{
return d->rulersMultiple2;
}
void KisGuidesConfig::setRulersMultiple2(bool value)
{
d->rulersMultiple2 = value;
}
KoUnit::Type KisGuidesConfig::unitType() const
{
return d->unitType;
}
void KisGuidesConfig::setUnitType(const KoUnit::Type type)
{
d->unitType = type;
}
KisGuidesConfig::LineTypeInternal
KisGuidesConfig::guidesLineType() const
{
......@@ -222,6 +249,10 @@ QDomElement KisGuidesConfig::saveToXml(QDomDocument& doc, const QString &tag) co
KisDomUtils::saveValue(&guidesElement, "horizontalGuides", d->horzGuideLines.toVector());
KisDomUtils::saveValue(&guidesElement, "verticalGuides", d->vertGuideLines.toVector());
KisDomUtils::saveValue(&guidesElement, "rulersMultiple2", d->rulersMultiple2);
KoUnit tmp(d->unitType);
KisDomUtils::saveValue(&guidesElement, "unit", tmp.symbol());
return guidesElement;
}
......@@ -242,5 +273,24 @@ bool KisGuidesConfig::loadFromXml(const QDomElement &parent)
d->horzGuideLines = QList<qreal>::fromVector(hGuides);
d->vertGuideLines = QList<qreal>::fromVector(vGuides);
result &= KisDomUtils::loadValue(parent, "rulersMultiple2", &d->rulersMultiple2);
QString unit;
result &= KisDomUtils::loadValue(parent, "unit", &unit);
bool ok = false;
KoUnit tmp = KoUnit::fromSymbol(unit, &ok);
if (ok) {
d->unitType = tmp.type();
}
result &= ok;
return result;
}
bool KisGuidesConfig::isDefault() const
{
KisGuidesConfig defaultObject;
defaultObject.loadStaticData();
return *this == defaultObject;
}
......@@ -26,6 +26,7 @@
#include <QScopedPointer>
#include <QList>
#include <boost/operators.hpp>
#include <KoUnit.h>
class QDomElement;
class QDomDocument;
......@@ -89,6 +90,12 @@ public:
bool snapToGuides() const;
void setSnapToGuides(bool value);
bool rulersMultiple2() const;
void setRulersMultiple2(bool value);
KoUnit::Type unitType() const;
void setUnitType(KoUnit::Type type);
LineTypeInternal guidesLineType() const;
void setGuidesLineType(LineTypeInternal value);
......@@ -111,6 +118,8 @@ public:
QDomElement saveToXml(QDomDocument& doc, const QString &tag) const;
bool loadFromXml(const QDomElement &parent);
bool isDefault() const;
private:
class Private;
const QScopedPointer<Private> d;
......
......@@ -166,6 +166,11 @@ void KisGuidesManager::setGuidesConfigImpl(const KisGuidesConfig &value, bool em
m_d->updateSnappingStatus(value);
}
if (m_d->view) {
m_d->view->document()->setUnit(KoUnit(m_d->guidesConfig.unitType()));
m_d->view->viewManager()->actionManager()->actionByName("ruler_pixel_multiple2")->setChecked(value.rulersMultiple2());
}
emit sigRequestUpdateGuidesConfig(m_d->guidesConfig);
}
......@@ -269,6 +274,28 @@ void KisGuidesManager::setSnapToGuides(bool value)
setGuidesConfigImpl(m_d->guidesConfig);
}
bool KisGuidesManager::rulersMultiple2() const
{
return m_d->guidesConfig.rulersMultiple2();
}
void KisGuidesManager::setRulersMultiple2(bool value)
{
m_d->guidesConfig.setRulersMultiple2(value);
setGuidesConfigImpl(m_d->guidesConfig);
}
KoUnit::Type KisGuidesManager::unitType() const
{
return m_d->guidesConfig.unitType();
}
void KisGuidesManager::setUnitType(const KoUnit::Type type)
{
m_d->guidesConfig.setUnitType(type);
setGuidesConfigImpl(m_d->guidesConfig);
}
void KisGuidesManager::setup(KisActionManager *actionManager)
{
KisAction *action = 0;
......
......@@ -22,6 +22,7 @@
#include <QScopedPointer>
#include <QObject>
#include "kritaui_export.h"
#include <KoUnit.h>
class KisView;
class KisActionManager;
......@@ -42,6 +43,9 @@ public:
bool showGuides() const;
bool lockGuides() const;
bool snapToGuides() const;
bool rulersMultiple2() const;
KoUnit::Type unitType() const;
bool eventFilter(QObject *obj, QEvent *event) override;
......@@ -55,6 +59,8 @@ public Q_SLOTS:
void setShowGuides(bool value);
void setLockGuides(bool value);
void setSnapToGuides(bool value);
void setRulersMultiple2(bool value);
void setUnitType(KoUnit::Type type);
void slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos);
void slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos);
......
......@@ -411,7 +411,6 @@ void KisShapeLayer::forceUpdateTimedNode()
#include "SvgWriter.h"
#include "SvgParser.h"
#include <QXmlStreamReader>
bool KisShapeLayer::saveShapesToStore(KoStore *store, QList<KoShape *> shapes, const QSizeF &sizeInPt)
{
......
......@@ -75,7 +75,7 @@ QPointF KisZoomAction::Private::centerPoint(QTouchEvent* event)
Q_FOREACH (QTouchEvent::TouchPoint point, event->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
result += point.screenPos();
result += point.pos();
count++;
}
}
......@@ -209,7 +209,7 @@ void KisZoomAction::inputEvent( QEvent* event )
if (point.state() != Qt::TouchPointReleased) {
count++;
dist += (point.screenPos() - center).manhattanLength();
dist += (point.pos() - center).manhattanLength();
}
}
......
......@@ -294,6 +294,7 @@ struct PenPointerItem
qreal oneOverDpr; // 1 / devicePixelRatio of activeWidget
bool widgetIsCaptured; // Current widget is capturing a pen cown event
bool widgetIsIgnored; // Pen events should be ignored until pen up
bool widgetAcceptsPenEvent; // Whether the widget accepts pen events
bool isCaptured() const {
return widgetIsCaptured;
......@@ -624,7 +625,57 @@ bool sendProximityTabletEvent(const QEvent::Type eventType, const POINTER_PEN_IN
return ev.isAccepted();
}
bool sendPositionalTabletEvent(QWidget *target, const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const PenPointerItem &penPointerItem)
void synthesizeMouseEvent(const QTabletEvent &ev, const POINTER_PEN_INFO &penInfo)
{
// Update the cursor position
BOOL result = SetCursorPos(penInfo.pointerInfo.ptPixelLocationRaw.x, penInfo.pointerInfo.ptPixelLocationRaw.y);
if (!result) {
dbgInput << "SetCursorPos failed, err" << GetLastError();
return;
}
// Send mousebutton down/up events. Windows stores the button state.
DWORD inputDataFlags = 0;
switch (ev.type()) {
case QEvent::TabletPress:
switch (ev.button()) {
case Qt::LeftButton:
inputDataFlags = MOUSEEVENTF_LEFTDOWN;
break;
case Qt::RightButton:
inputDataFlags = MOUSEEVENTF_RIGHTDOWN;
break;
default:
return;
}
break;
case QEvent::TabletRelease:
switch (ev.button()) {
case Qt::LeftButton:
inputDataFlags = MOUSEEVENTF_LEFTUP;
break;
case Qt::RightButton:
inputDataFlags = MOUSEEVENTF_RIGHTUP;
break;
default:
return;
}
break;
case QEvent::TabletMove:
default:
return;
}
INPUT inputData = {};
inputData.type = INPUT_MOUSE;
inputData.mi.dwFlags = inputDataFlags;
inputData.mi.dwExtraInfo = 0xFF515700 | 0x01; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320%28v=vs.85%29.aspx
UINT result2 = SendInput(1, &inputData, sizeof(inputData));
if (result2 != 1) {
dbgInput << "SendInput failed, err" << GetLastError();
return;
}
}
bool sendPositionalTabletEvent(QWidget *target, const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const PenPointerItem &penPointerItem, const bool shouldSynthesizeMouseEvent)
{
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
eventType == QEvent::TabletMove || eventType == QEvent::TabletPress || eventType == QEvent::TabletRelease,
......@@ -634,7 +685,43 @@ bool sendPositionalTabletEvent(QWidget *target, const QEvent::Type eventType, co
ev.setAccepted(false);
ev.setTimestamp(penInfo.pointerInfo.dwTime);
QCoreApplication::sendEvent(target, &ev);
return ev.isAccepted();
if (!shouldSynthesizeMouseEvent) {
// For pen update with multiple updates, only the last update should
// be used to synthesize a mouse event.
return false;
}
// This is some specialized code to handle synthesizing of mouse events from
// the pen events. Issues being:
// 1. We must accept the pointer down/up and the intermediate update events
// to indicate that we want all the pen pointer events for painting,
// otherwise Windows may do weird stuff and skip passing pointer events.
// 2. Some Qt and Krita code uses QCursor::pos() which calls GetCursorPos to
// get the cursor position. This doesn't work nicely before ver 1709 and
// doesn't work at all on ver 1709 if the pen events are handled, so we
// need some way to nudge the cursor on the OS level.
// It appears that using the same way (as in synthesizeMouseEvent) to nudge
// the cursor does work fine for when painting on canvas (which handles
// the QTabletEvent), but not for other widgets because it introduces a lag
// with mouse move events on move start and immediately after mouse down.
// The resolution is to simulate mouse movement with our own code only for
// handled pen events, which is what the following code does.
if (ev.type() == QEvent::TabletMove && ev.buttons() == Qt::NoButton) {
// Let Windows synthesize mouse hover events
return false;
}
if (ev.type() == QEvent::TabletPress && !ev.isAccepted()) {
// On pen down event, if the widget doesn't handle the event, let
// Windows translate the event to touch, mouse or whatever
return false;
}
if (ev.type() != QEvent::TabletPress && !penPointerItem.widgetAcceptsPenEvent) {
// For other events, if the previous pen down event wasn't handled by
// the widget, continue to let Windows translate the event
return false;
}
// Otherwise, we synthesize our mouse events
synthesizeMouseEvent(ev, penInfo);
return true; // and tell Windows that we do want the pen events.
}
bool handlePenEnterMsg(const POINTER_PEN_INFO &penInfo)
......@@ -666,6 +753,7 @@ bool handlePenEnterMsg(const POINTER_PEN_INFO &penInfo)
penPointerItem.oneOverDpr = 1.0;
penPointerItem.widgetIsCaptured = false;
penPointerItem.widgetIsIgnored = false;
penPointerItem.widgetAcceptsPenEvent = false;
// penPointerItem.pointerId = pointerId;
penPointers.insert(pointerId, penPointerItem);
......@@ -698,7 +786,7 @@ bool handlePenLeaveMsg(const POINTER_PEN_INFO &penInfo)
return false;
}
bool handleSinglePenUpdate(PenPointerItem &penPointerItem, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device)
bool handleSinglePenUpdate(PenPointerItem &penPointerItem, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const bool shouldSynthesizeMouseEvent)
{
QWidget *targetWidget;
if (penPointerItem.isCaptured()) {
......@@ -745,10 +833,7 @@ bool handleSinglePenUpdate(PenPointerItem &penPointerItem, const POINTER_PEN_INF
// penPointerItem.activeWidget = targetWidget;
}
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletMove, penInfo, device, penPointerItem);
if (!handled) {
// dbgTablet << "Target widget doesn't want pen events";
}
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletMove, penInfo, device, penPointerItem, shouldSynthesizeMouseEvent);
return handled;
}
......@@ -773,6 +858,7 @@ bool handlePenUpdateMsg(const POINTER_PEN_INFO &penInfo)
// }
UINT32 entriesCount = penInfo.pointerInfo.historyCount;
// dbgTablet << "entriesCount:" << entriesCount;
bool handled = false;
if (entriesCount != 1) {
QVector<POINTER_PEN_INFO> penInfoArray(entriesCount);
if (!api.GetPointerPenInfoHistory(penInfo.pointerInfo.pointerId, &entriesCount, penInfoArray.data())) {
......@@ -783,13 +869,14 @@ bool handlePenUpdateMsg(const POINTER_PEN_INFO &penInfo)
// The returned array is in reverse chronological order
const auto rbegin = penInfoArray.rbegin();
const auto rend = penInfoArray.rend();
const auto rlast = rend - 1; // Only synthesize mouse event for the last one
for (auto it = rbegin; it != rend; ++it) {
handled |= handleSinglePenUpdate(*currentPointerIt, *it, *devIt); // Bitwise OR doesn't short circuit
handled |= handleSinglePenUpdate(*currentPointerIt, *it, *devIt, it == rlast); // Bitwise OR doesn't short circuit
}
return handled;
} else {
return handleSinglePenUpdate(*currentPointerIt, penInfo, *devIt);
handled = handleSinglePenUpdate(*currentPointerIt, penInfo, *devIt, true);
}
return handled;
}
bool handlePenDownMsg(const POINTER_PEN_INFO &penInfo)
......@@ -851,7 +938,8 @@ bool handlePenDownMsg(const POINTER_PEN_INFO &penInfo)
return false;
}
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletPress, penInfo, *devIt, *currentPointerIt);
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletPress, penInfo, *devIt, *currentPointerIt, true);
currentPointerIt->widgetAcceptsPenEvent = handled;
if (!handled) {
// dbgTablet << "QWidget did not handle tablet down event";
}
......@@ -891,10 +979,11 @@ bool handlePenUpMsg(const POINTER_PEN_INFO &penInfo)
return false;
}
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletRelease, penInfo, *devIt, penPointerItem);
bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletRelease, penInfo, *devIt, penPointerItem, true);
// dbgTablet << "QWidget" << currentPointerIt->activeWidget->windowTitle() << "is releasing capture to pointer" << penInfo.pointerInfo.pointerId;
penPointerItem.widgetIsCaptured = false;
penPointerItem.widgetAcceptsPenEvent = false;
return handled;
}
......@@ -979,9 +1068,7 @@ bool handlePointerMsg(const MSG &msg)
case WM_POINTERLEAVE:
return handlePenLeaveMsg(penInfo);
case WM_POINTERUPDATE:
// HACK: Force further processing to force Windows to generate mouse move events
handlePenUpdateMsg(penInfo);
return false;
return handlePenUpdateMsg(penInfo);
case WM_POINTERCAPTURECHANGED:
// TODO: Should this event be handled?
dbgTablet << "FIXME: WM_POINTERCAPTURECHANGED isn't handled";
......
......@@ -330,6 +330,7 @@ QByteArray KisPaintingAssistant::saveXml(QMap<KisPaintingAssistantHandleSP, int>
xml.writeStartDocument();
xml.writeStartElement("assistant");
xml.writeAttribute("type",d->id);
xml.writeAttribute("active", QString::number(d->isSnappingActive));
xml.writeStartElement("handles");
Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) {
int id = handleMap.size();
......@@ -359,6 +360,11 @@ void KisPaintingAssistant::loadXml(KoStore* store, QMap<int, KisPaintingAssistan
while (!xml.atEnd()) {
switch (xml.readNext()) {
case QXmlStreamReader::StartElement:
if (xml.name() == "assistant") {
QStringRef active = xml.attributes().value("active");
d->isSnappingActive = (active != "0");
}
if (xml.name() == "handle") {
QString strId = xml.attributes().value("id").toString(),
strX = xml.attributes().value("x").toString(),
......
......@@ -48,6 +48,8 @@
#include "kis_canvas_resource_provider.h"
#include "kis_lod_transform.h"
#include "kis_snap_line_strategy.h"
#include "kis_guides_config.h"
#include "kis_guides_manager.h"
class KisZoomController : public KoZoomController
......@@ -133,11 +135,18 @@ void KisZoomManager::setup(KActionCollection * actionCollection)
m_verticalRuler->setVisible(false);
QAction *rulerAction = actionCollection->action("ruler_pixel_multiple2");
if (m_view->document()->guidesConfig().rulersMultiple2()) {
m_horizontalRuler->setUnitPixelMultiple2(true);
m_verticalRuler->setUnitPixelMultiple2(true);
}
QList<QAction*> unitActions = m_view->createChangeUnitActions(true);
unitActions.append(rulerAction);
m_horizontalRuler->setPopupActionList(unitActions);
m_verticalRuler->setPopupActionList(unitActions);
connect(m_view->document(), SIGNAL(unitChanged(const KoUnit&)), SLOT(applyRulersUnit(const KoUnit&)));
connect(rulerAction, SIGNAL(toggled(bool)), SLOT(setRulersPixelMultiple2(bool)));
layout->addWidget(m_horizontalRuler, 0, 1);
layout->addWidget(m_verticalRuler, 1, 0);
......@@ -252,6 +261,18 @@ void KisZoomManager::applyRulersUnit(const KoUnit &baseUnit)
m_horizontalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->xRes()));
m_verticalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->yRes()));
}
if (m_view->viewManager()) {
m_view->viewManager()->guidesManager()->setUnitType(baseUnit.type());
}
}
void KisZoomManager::setRulersPixelMultiple2(bool enabled)
{
m_horizontalRuler->setUnitPixelMultiple2(enabled);
m_verticalRuler->setUnitPixelMultiple2(enabled);
if (m_view->viewManager()) {
m_view->viewManager()->guidesManager()->setRulersMultiple2(enabled);
}
}
void KisZoomManager::setMinMaxZoom()
......
......@@ -82,6 +82,7 @@ public Q_SLOTS:
void zoomTo100();
void applyRulersUnit(const KoUnit &baseUnit);
void setMinMaxZoom();
void setRulersPixelMultiple2(bool enabled);
private:
......
......@@ -709,7 +709,8 @@ KoRulerPrivate::KoRulerPrivate(KoRuler *parent, const KoViewConverter *vc, Qt::O
distancesPaintingStrategy((PaintingStrategy*)new HorizontalDistancesPaintingStrategy()),
paintingStrategy(normalPaintingStrategy),
ruler(parent),
guideCreationStarted(false)
guideCreationStarted(false),
pixelStep(100.0)
{
}
......@@ -732,7 +733,7 @@ qreal KoRulerPrivate::numberStepForUnit() const
return 10.0;
case KoUnit::Point:
default:
return 100.0;
return pixelStep;
}
}
......@@ -1368,3 +1369,13 @@ void KoRuler::createGuideToolConnection(KoCanvasBase *canvas)
connect(this, SIGNAL(guideLineCreated(Qt::Orientation,qreal)),
tool, SLOT(createGuideLine(Qt::Orientation,qreal)));
}
void KoRuler::setUnitPixelMultiple2(bool enabled)
{
if (enabled) {
d->pixelStep = 64.0;
}
else {
d->pixelStep = 100.0;
}
}
......@@ -239,6 +239,8 @@ public Q_SLOTS:
*/
void createGuideToolConnection(KoCanvasBase *canvas);
void setUnitPixelMultiple2(bool enabled);
Q_SIGNALS:
/**
* emitted when any of the indents is moved by the user.
......
......@@ -195,6 +195,8 @@ public:
bool guideCreationStarted;
qreal pixelStep;
qreal numberStepForUnit() const;
/// @return The rounding of value to the nearest multiple of stepValue
qreal doSnapping(const qreal value) const;
......
......@@ -223,6 +223,11 @@ if not "%1" == "" (
goto usage_and_fail
)
call :get_full_path ARG_PRE_ZIP_HOOK "%~f2"
if "!ARG_PRE_ZIP_HOOK!" == "" (
echo ERROR: Arg --pre-zip-hook does not point to a valid file 1>&2
echo.
goto usage_and_fail
)
shift /2
set CURRENT_MATCHED=1
)
......@@ -240,7 +245,7 @@ if not "%1" == "" (
if "%ARG_NO_INTERACTIVE%" == "1" (
if "%ARG_PACKAGE_NAME%" == "" (
echo ERROR: Required arg --package-name not specified1>&2
echo ERROR: Required arg --package-name not specified! 1>&2
echo.
goto usage_and_fail
)
......@@ -680,7 +685,7 @@ endlocal
if not "%ARG_PRE_ZIP_HOOK%" == "" (
echo Running pre-zip-hook...