8000 Add laser/temporary stroke tool for presentations by bhennion · Pull Request #6391 · xournalpp/xournalpp · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add laser/temporary stroke tool for presentations #6391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/core/control/ToolEnums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ auto toolTypeToString(ToolType type) -> std::string {
return "selectPdfTextLinear";
case TOOL_SELECT_PDF_TEXT_RECT:
return "selectPdfTextRect";
case TOOL_LASER_POINTER:
return "laserPointer";
default:
return "";
}
Expand Down Expand Up @@ -235,6 +237,9 @@ auto toolTypeFromString(const std::string& type) -> ToolType {
if (type == "selectPdfTextRect") {
return TOOL_SELECT_PDF_TEXT_RECT;
}
if (type == "laserPointer") {
return TOOL_LASER_POINTER;
}
return TOOL_NONE;
}

Expand Down
1 change: 1 addition & 0 deletions src/core/control/ToolEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum ToolType {
TOOL_DRAW_SPLINE = 20,
TOOL_SELECT_PDF_TEXT_LINEAR = 21,
TOOL_SELECT_PDF_TEXT_RECT = 22,
TOOL_LASER_POINTER = 23,

TOOL_END_ENTRY
};
Expand Down
8 changes: 8 additions & 0 deletions src/core/control/ToolHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ void ToolHandler::initTools() {
tools[TOOL_SELECT_PDF_TEXT_RECT - TOOL_PEN] =
std::make_unique<ToolSelectPDFText>("selectPdfTextRect", TOOL_SELECT_PDF_TEXT_RECT, Colors::black);

thickness[TOOL_SIZE_VERY_FINE] = 1;
thickness[TOOL_SIZE_FINE] = 2.83;
thickness[TOOL_SIZE_MEDIUM] = 8.50;
thickness[TOOL_SIZE_THICK] = 12;
thickness[TOOL_SIZE_VERY_THICK] = 18;
tools[TOOL_LASER_POINTER - TOOL_PEN] = std::make_unique<Tool>("laserPointer", TOOL_LASER_POINTER, Colors::red,
TOOL_CAP_COLOR | TOOL_CAP_SIZE, thickness);

this->eraserButtonTool = std::make_unique<Tool>(*tools[TOOL_HIGHLIGHTER - TOOL_PEN]);
this->stylusButton1Tool = std::make_unique<Tool>(*tools[TOOL_HIGHLIGHTER - TOOL_PEN]);
this->stylusButton2Tool = std::make_unique<Tool>(*tools[TOOL_HIGHLIGHTER - TOOL_PEN]);
Expand Down
20 changes: 19 additions & 1 deletion src/core/control/settings/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ void Settings::loadDefault() {
this->useSpacesForTab = false;
this->numberOfSpacesForTab = 4;

this->laserPointerFadeOutTime = 500;

this->colorPaletteSetting = Util::getBuiltInPaletteDirectoryPath() / DEFAULT_PALETTE_FILE;
}

Expand Down Expand Up @@ -670,7 +672,11 @@ void Settings::parseItem(xmlDocPtr doc, xmlNodePtr cur) {
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("useSpacesForTab")) == 0) {
this->setUseSpacesAsTab(xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numberOfSpacesForTab")) == 0) {
this->setNumberOfSpacesForTab(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
this->setNumberOfSpacesForTab(
static_cast<unsigned int>(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10)));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("laserPointerFadeOutTime")) == 0) {
this->laserPointerFadeOutTime =
static_cast<unsigned int>(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
/**
* Stabilizer related settings
*/
Expand Down Expand Up @@ -1149,6 +1155,8 @@ void Settings::save() {
SAVE_BOOL_PROP(useSpacesForTab);
SAVE_UINT_PROP(numberOfSpacesForTab);

SAVE_UINT_PROP(laserPointerFadeOutTime);

/**
* Stabilizer related settings
*/
Expand Down Expand Up @@ -2641,3 +2649,13 @@ void Settings::setNumberOfSpacesForTab(unsigned int numberOfSpaces) {
}

unsigned int Settings::getNumberOfSpacesForTab() const { return this->numberOfSpacesForTab; }

void Settings::setLaserPointerFadeOutTime(unsigned int timeInMs) {
if (this->laserPointerFadeOutTime == timeInMs) {
return;
}
this->laserPointerFadeOutTime = timeInMs;
save();
}

unsigned int Settings::getLaserPointerFadeOutTime() const { return this->laserPointerFadeOutTime; }
5 changes: 5 additions & 0 deletions src/core/control/settings/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,9 @@ class Settings {
void setUseSpacesAsTab(bool useSpaces);
bool getUseSpacesAsTab() const;

void setLaserPointerFadeOutTime(unsigned int timeInMs);
unsigned int getLaserPointerFadeOutTime() const;

public:
// Custom settings
SElement& getCustomElement(const std::string& name);
Expand Down Expand Up @@ -1166,4 +1169,6 @@ class Settings {
*/
bool useSpacesForTab{};
unsigned int numberOfSpacesForTab{};

unsigned int laserPointerFadeOutTime{}; ///< Time in ms before the laser pointer strokes start fading out
};
97 changes: 97 additions & 0 deletions src/core/control/tools/LaserPointerHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include "LaserPointerHandler.h"

#include <control/Control.h>
#include <control/settings/Settings.h>
#include <gui/MainWindow.h>
#include <gui/PageView.h>
#include <gui/XournalView.h>

#include "gui/inputdevices/PositionInputData.h" // for PositionInputData
#include "model/Stroke.h"
#include "util/DispatchPool.h"
#include "util/glib_casts.h"
#include "view/overlays/LaserPointerView.h"

#include "StrokeHandler.h"

static constexpr int FADEOUT_STEP_DURATION = 50; ///< in ms
static constexpr uint8_t FADEOUT_ALPHA_STEP = 25;
// The total fadeout duration will be FADEOUT_STEP_DURATION * 255 / FADEOUT_ALPHA_STEP

class TemporaryStrokeHandler: public StrokeHandler {
public:
TemporaryStrokeHandler(Control* control, const PageRef& page);
~TemporaryStrokeHandler() override;
void finalize(double pressure);
};

TemporaryStrokeHandler::TemporaryStrokeHandler(Control* control, const PageRef& page): StrokeHandler(control, page) {}
TemporaryStrokeHandler::~TemporaryStrokeHandler() = default;

void TemporaryStrokeHandler::finalize(double pressure) { this->finalizeStroke(pressure); }

LaserPointerHandler::LaserPointerHandler(XojPageView* pageView, Control* control, const PageRef& page):
viewPool(std::make_shared<xoj::util::DispatchPool<xoj::view::LaserPointerView>>()),
ctrl(control),
page(page),
pageView(pageView),
fadeoutStartDelay(control->getSettings()->getLaserPointerFadeOutTime()) {}

LaserPointerHandler::~LaserPointerHandler() = default;

std::unique_ptr<xoj::view::OverlayView> LaserPointerHandler::createView(xoj::view::Repaintable* parent) const {
auto view = std::make_unique<xoj::view::LaserPointerView>(this, parent);
if (this->strokehandler) {
view->on(xoj::view::LaserPointerView::START_NEW_STROKE_REQUEST, this->strokehandler.get());
}
return view;
}

void LaserPointerHandler::onButtonPressEvent(const PositionInputData& pos, double zoom) {
this->strokehandler = std::make_unique<TemporaryStrokeHandler>(ctrl, page);
this->strokehandler->onButtonPressEvent(pos, zoom);
this->viewPool->dispatch(xoj::view::LaserPointerView::START_NEW_STROKE_REQUEST, strokehandler.get());

this->fadeoutTimer.cancel();
this->fadeoutAlpha = 255;
}

void LaserPointerHandler::onButtonReleaseEvent(const PositionInputData& pos, double zoom) {
xoj_assert(this->strokehandler);
this->strokehandler->finalize(pos.pressure);
this->viewPool->dispatch(xoj::view::LaserPointerView::FINISH_STROKE_REQUEST,
Range(this->strokehandler->getStroke()->boundingRect()));
this->strokehandler.reset();
this->fadeoutTimer =
g_timeout_add(this->fadeoutStartDelay, xoj::util::wrap_for_once_v<triggerFadeoutCallback>, this);
}

bool LaserPointerHandler::onMotionNotifyEvent(const PositionInputData& pos, double zoom) {
return this->strokehandler ? this->strokehandler->onMotionNotifyEvent(pos, zoom) : false;
}

void LaserPointerHandler::onSequenceCancelEvent() {
auto s = std::move(this->strokehandler);
if (s && s->getStroke()) {
Range rg(s->getStroke()->boundingRect());
this->viewPool->dispatch(xoj::view::LaserPointerView::INPUT_CANCELLATION_REQUEST, rg);
}
}

void LaserPointerHandler::triggerFadeoutCallback(LaserPointerHandler* self) {
self->fadeoutTimer.consume();
self->fadeoutTimer = g_timeout_add(FADEOUT_STEP_DURATION, xoj::util::wrap_v<fadeoutCallback>, self);
}

gboolean LaserPointerHandler::fadeoutCallback(LaserPointerHandler* self) {
if (self->fadeoutAlpha <= FADEOUT_ALPHA_STEP) {
self->fadeoutAlpha = 0;
self->viewPool->dispatchAndClear(xoj::view::LaserPointerView::FINALIZATION_REQUEST);
self->fadeoutTimer.consume();
self->pageView->deleteLaserPointerHandler(); // WARNING: deletes *self
return G_SOURCE_REMOVE;
}
self->fadeoutAlpha -= FADEOUT_ALPHA_STEP;
self->viewPool->dispatch(xoj::view::LaserPointerView::SET_ALPHA_REQUEST, self->fadeoutAlpha);
return G_SOURCE_CONTINUE; // The timer will run again and call this callback once more (or be cancelled)
}
69 changes: 69 additions & 0 deletions src/core/control/tools/LaserPointerHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Xournal++
*
* Handles a laser pointer that draws temporary strokes
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/

#pragma once

#include <memory> // for unique_ptr

#include "model/OverlayBase.h"
#include "model/PageRef.h" // for PageRef
#include "util/raii/GSourceURef.h"

class Control;
class PositionInputData;
class TemporaryStrokeHandler;
class XojPageView;

namespace xoj::util {
template <class T>
class DispatchPool;
};


namespace xoj::view {
class OverlayView;
class Repaintable;
class LaserPointerView;
}; // namespace xoj::view

class LaserPointerHandler: public OverlayBase {
public:
LaserPointerHandler(XojPageView* pageView, Control* control, const PageRef& page);
~LaserPointerHandler() override;

void onSequenceCancelEvent();
bool onMotionNotifyEvent(const PositionInputData& pos, double zoom);
void onButtonReleaseEvent(const PositionInputData& pos, double zoom);
void onButtonPressEvent(const PositionInputData& pos, double zoom);

auto createView(xoj::view::Repaintable* parent) const -> std::unique_ptr<xoj::view::OverlayView>;

inline auto getViewPool() const -> std::shared_ptr<xoj::util::DispatchPool<xoj::view::LaserPointerView>> {
return viewPool;
}

private:
static void triggerFadeoutCallback(LaserPointerHandler* self);
static gboolean fadeoutCallback(LaserPointerHandler* self);

private:
std::unique_ptr<TemporaryStrokeHandler> strokehandler;
std::shared_ptr<xoj::util::DispatchPool<xoj::view::LaserPointerView>> viewPool;

// Used only to pass on to (Temporary)StrokeHandler
Control* ctrl;
PageRef page;
XojPageView* pageView;

xoj::util::GSourceURef fadeoutTimer;
uint8_t fadeoutAlpha = 255;
const unsigned int fadeoutStartDelay;
};
11 changes: 9 additions & 2 deletions src/core/control/tools/StrokeHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ void StrokeHandler::onSequenceCancelEvent() {
}
}

void StrokeHandler::onButtonReleaseEvent(const PositionInputData& pos, double zoom) {
void StrokeHandler::finalizeStroke(double pressure) {
if (!stroke) {
return;
}
Expand All @@ -148,14 +148,21 @@ void StrokeHandler::onButtonReleaseEvent(const PositionInputData& pos, double zo
const Point pt = pv.front(); // Make a copy, otherwise stroke->addPoint(pt); in UB
if (this->hasPressure) {
// Pressure inference provides a pressure value to the last event. Most devices set this value to 0.
const double newPressure = std::max(pt.z, pos.pressure * this->stroke->getWidth());
const double newPressure = std::max(pt.z, pressure * this->stroke->getWidth());
this->stroke->setLastPressure(newPressure);
this->viewPool->dispatch(xoj::view::StrokeToolView::THICKEN_FIRST_POINT_REQUEST, newPressure);
}
stroke->addPoint(pt);
}

stroke->freeUnusedPointItems();
}

void StrokeHandler::onButtonReleaseEvent(const PositionInputData& pos, double zoom) {
if (!stroke) {
return;
}
finalizeStroke(pos.pressure);

Layer* layer = page->getSelectedLayer();

Expand Down
3 changes: 3 additions & 0 deletions src/core/control/tools/StrokeHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class StrokeHandler: public InputHandler {

void strokeRecognizerDetected(std::unique_ptr<Stroke> recognized, Layer* layer);

/// Finalizes the stroke using the provided pressure as last point
void finalizeStroke(double pressure);

protected:
Point buttonDownPoint; // used for tapSelect and filtering - never snapped to grid.
SnapToGridInputHandler snappingHandler;
Expand Down
20 changes: 20 additions & 0 deletions src/core/gui/PageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "control/tools/ImageHandler.h" // for ImageHandler
#include "control/tools/ImageSizeSelection.h" // for ImageSizeSelection
#include "control/tools/InputHandler.h" // for InputHandler
#include "control/tools/LaserPointerHandler.h" // for LaserPointerHandler
#include "control/tools/PdfElemSelection.h" // for PdfElemSelection
#include "control/tools/RectangleHandler.h" // for RectangleHandler
#include "control/tools/RulerHandler.h" // for RulerHandler
Expand Down Expand Up @@ -211,6 +212,11 @@ void XojPageView::endSpline() {
}
}

void XojPageView::deleteLaserPointerHandler() {
xoj_assert(hasNoViewOf(overlayViews, laserPointer.get()));
laserPointer.reset();
}

auto XojPageView::onButtonPressEvent(const PositionInputData& pos) -> bool {
if (currentSequenceDeviceId) {
// An input sequence is already under way from another device
Expand Down Expand Up @@ -294,6 +300,14 @@ auto XojPageView::onButtonPressEvent(const PositionInputData& pos) -> bool {
} else if (h->getToolType() == TOOL_ERASER) {
this->eraser->erase(x, y);
this->inEraser = true;
} else if (h->getToolType() == TOOL_LASER_POINTER) {
if (!this->laserPointer) {
this->laserPointer = std::make_unique<LaserPointerHandler>(this, control, getPage());
this->laserPointer->onButtonPressEvent(pos, zoom);
this->overlayViews.emplace_back(this->laserPointer->createView(this));
} else {
this->laserPointer->onButtonPressEvent(pos, zoom);
}
} else if (h->getToolType() == TOOL_VERTICAL_SPACE) {
if (this->verticalSpace) {
control->getUndoRedoHandler()->addUndoAction(this->verticalSpace->finalize());
Expand Down Expand Up @@ -545,6 +559,8 @@ auto XojPageView::onMotionNotifyEvent(const PositionInputData& pos) -> bool {

const Text* text = this->textEditor->getTextElement();
this->textEditor->mouseMoved(x - text->getX(), y - text->getY());
} else if (this->laserPointer && this->laserPointer->onMotionNotifyEvent(pos, zoom)) {
// used this event
} else if (h->getToolType() == TOOL_ERASER && h->getEraserType() != ERASER_TYPE_WHITEOUT && this->inEraser) {
this->eraser->erase(x, y);
}
Expand Down Expand Up @@ -572,6 +588,8 @@ void XojPageView::onSequenceCancelEvent(DeviceId deviceId) {
xoj_assert(hasNoViewOf(overlayViews, inputHandler.get()));
this->inputHandler.reset();
}
} else if (this->laserPointer) {
this->laserPointer->onSequenceCancelEvent();
}
}

Expand Down Expand Up @@ -657,6 +675,8 @@ auto XojPageView::onButtonReleaseEvent(const PositionInputData& pos) -> bool {
xoj_assert(hasNoViewOf(overlayViews, inputHandler.get()));
this->inputHandler.reset();
}
} else if (this->laserPointer) {
this->laserPointer->onButtonReleaseEvent(pos, xournal->getZoom());
4FDF }

if (this->inEraser) {
Expand Down
Loading
0